还是以前面的HelloWord服务为例子。
服务端:
HelloWord.java
package ch03.ts; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.xml.ws.Holder; @WebService public interface HelloWord { @WebMethod void sayHello(@WebParam(name="name") String name, @WebParam(name="wh",mode=WebParam.Mode.INOUT) Holder<String> wh, @WebParam(name="hf",mode=WebParam.Mode.OUT) Holder<String> hf); }
HelloWordImpl.java(注意:这里添加了@BindingType注解)
package ch03.ts; import javax.jws.HandlerChain; import javax.jws.WebService; import javax.xml.ws.BindingType; import javax.xml.ws.Holder; @WebService(endpointInterface = "ch03.ts.HelloWord") @HandlerChain(file = "handler-chain-server.xml") @BindingType(value = "http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/") public class HelloWordImpl implements HelloWord { @Override public void sayHello(String name, Holder<String> wh, Holder<String> hf) { System.out.println(name + "!" + wh.value); wh.value = "你们好"; hf.value = "同学们"; } }
UUIDValidator.java
package fibC; import java.util.Iterator; import java.util.Set; import java.util.UUID; import javax.xml.namespace.QName; import javax.xml.soap.Node; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPConstants; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import javax.xml.ws.soap.SOAPFaultException; /** * 服务器端验证UUID值 * @author fuhd */ public class UUIDValidator implements SOAPHandler<SOAPMessageContext> { private static final int UUIDVARIANT = 2; //layout private static final int UUIDVERSION = 4; //version @SuppressWarnings({ "rawtypes"}) @Override public boolean handleMessage(SOAPMessageContext context) { Boolean resp = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if(!resp){ try { SOAPMessage msg = context.getMessage(); SOAPEnvelope env = msg.getSOAPPart().getEnvelope(); SOAPHeader hdr = env.getHeader(); if(hdr == null) generateSOAPFault(msg, "No message header."); Iterator it = hdr.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT); if(it == null || !it.hasNext()) generateSOAPFault(msg, "No header block for next actor."); Node next = (Node)it.next(); String value = (next == null)?null:next.getValue(); if(value == null) generateSOAPFault(msg, "No UUID in header block."); UUID uuid = UUID.fromString(value.trim()); if(uuid.variant() != UUIDVARIANT || uuid.version() != UUIDVERSION) generateSOAPFault(msg, "Bad UUID variant or version"); System.out.println(value.trim()); } catch (SOAPException e) { e.printStackTrace(); } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { return true; } @Override public void close(MessageContext context) {} @Override public Set<QName> getHeaders() { return null; } private void generateSOAPFault(SOAPMessage msg,String reason){ try { SOAPBody body = msg.getSOAPPart().getEnvelope().getBody(); SOAPFault fault = body.addFault(); fault.setFaultString(reason); throw new SOAPFaultException(fault); } catch (SOAPException e) { e.printStackTrace(); } } }
handler-chain-server.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class>fibC.UUIDValidator</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
HelloWordPublisher.java
package ch03.ts; import javax.xml.ws.Endpoint; public class HelloWordPublisher { public static void main(String[] args) { Endpoint.publish("http://localhost:7654/ts", new HelloWordImpl()); } }
我们再通过wsimport命令生成客户端代码:
% wsimport -keep -extension -p hw5 http://localhost:7654/ts?wsdl
注意上面 -extension 这个参数。
生成的客户端代码有:
HelloWord.java
package hw5; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.ws.Action; import javax.xml.ws.Holder; import javax.xml.ws.RequestWrapper; import javax.xml.ws.ResponseWrapper; /** * This class was generated by the JAX-WS RI. * JAX-WS RI 2.2.4-b01 * Generated source version: 2.2 */ @WebService(name = "HelloWord", targetNamespace = "http://ts.ch03/") @XmlSeeAlso({ ObjectFactory.class }) public interface HelloWord { /** * @param wh * @param name * @param hf */ @WebMethod @RequestWrapper(localName = "sayHello", targetNamespace = "http://ts.ch03/", className = "hw5.SayHello") @ResponseWrapper(localName = "sayHelloResponse", targetNamespace = "http://ts.ch03/", className = "hw5.SayHelloResponse") @Action(input = "http://ts.ch03/HelloWord/sayHelloRequest", output = "http://ts.ch03/HelloWord/sayHelloResponse") public void sayHello( @WebParam(name = "name", targetNamespace = "") String name, @WebParam(name = "wh", targetNamespace = "", mode = WebParam.Mode.INOUT) Holder<String> wh, @WebParam(name = "hf", targetNamespace = "", mode = WebParam.Mode.OUT) Holder<String> hf); }
HelloWordImplService.java
package hw5; import java.net.MalformedURLException; import java.net.URL; import javax.jws.HandlerChain; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.WebEndpoint; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebServiceException; import javax.xml.ws.WebServiceFeature; /** * This class was generated by the JAX-WS RI. * JAX-WS RI 2.2.4-b01 * Generated source version: 2.2 */ @WebServiceClient(name = "HelloWordImplService", targetNamespace = "http://ts.ch03/", wsdlLocation = "http://localhost:7654/ts?wsdl") @HandlerChain(file = "handler-chain.xml") public class HelloWordImplService extends Service { private final static URL HELLOWORDIMPLSERVICE_WSDL_LOCATION; private final static WebServiceException HELLOWORDIMPLSERVICE_EXCEPTION; private final static QName HELLOWORDIMPLSERVICE_QNAME = new QName("http://ts.ch03/", "HelloWordImplService"); static { URL url = null; WebServiceException e = null; try { url = new URL("http://localhost:7654/ts?wsdl"); } catch (MalformedURLException ex) { e = new WebServiceException(ex); } HELLOWORDIMPLSERVICE_WSDL_LOCATION = url; HELLOWORDIMPLSERVICE_EXCEPTION = e; } public HelloWordImplService() { super(__getWsdlLocation(), HELLOWORDIMPLSERVICE_QNAME); } public HelloWordImplService(WebServiceFeature... features) { super(__getWsdlLocation(), HELLOWORDIMPLSERVICE_QNAME, features); } public HelloWordImplService(URL wsdlLocation) { super(wsdlLocation, HELLOWORDIMPLSERVICE_QNAME); } public HelloWordImplService(URL wsdlLocation, WebServiceFeature... features) { super(wsdlLocation, HELLOWORDIMPLSERVICE_QNAME, features); } public HelloWordImplService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public HelloWordImplService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) { super(wsdlLocation, serviceName, features); } /** * @return * returns HelloWord */ @WebEndpoint(name = "HelloWordImplPort") public HelloWord getHelloWordImplPort() { return super.getPort(new QName("http://ts.ch03/", "HelloWordImplPort"), HelloWord.class); } /** * @param features * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. * Supported features not in the <code>features</code> parameter will have * their default values. * @return * returns HelloWord */ @WebEndpoint(name = "HelloWordImplPort") public HelloWord getHelloWordImplPort(WebServiceFeature... features) { return super.getPort(new QName("http://ts.ch03/", "HelloWordImplPort"), HelloWord.class, features); } private static URL __getWsdlLocation() { if (HELLOWORDIMPLSERVICE_EXCEPTION!= null) { throw HELLOWORDIMPLSERVICE_EXCEPTION; } return HELLOWORDIMPLSERVICE_WSDL_LOCATION; } }
SayHello.java
package hw5; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for sayHello complex type. * <p>The following schema fragment specifies the expected content * contained within this class. * <pre> * <complexType name="sayHello"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string" * minOccurs="0"/> * <element name="wh" type="{http://www.w3.org/2001/XMLSchema}string" * minOccurs="0"/> * </sequence> * </restriction> * </complexContent> * </complexType> * </pre> */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "sayHello", propOrder = { "name", "wh" }) public class SayHello { protected String name; protected String wh; /** * Gets the value of the name property. * @return * possible object is * {@link String } */ public String getName() { return name; } /** * Sets the value of the name property. * @param value * allowed object is * {@link String } */ public void setName(String value) { this.name = value; } /** * Gets the value of the wh property. * @return * possible object is * {@link String } */ public String getWh() { return wh; } /** * Sets the value of the wh property. * @param value * allowed object is * {@link String } */ public void setWh(String value) { this.wh = value; } }
SayHelloResponse.java
package hw5; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for sayHelloResponse complex type. * <p>The following schema fragment specifies the expected content contained * within this class. * <pre> * <complexType name="sayHelloResponse"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="wh" type="{http://www.w3.org/2001/XMLSchema}string" * minOccurs="0"/> * <element name="hf" type="{http://www.w3.org/2001/XMLSchema}string" * minOccurs="0"/> * </sequence> * </restriction> * </complexContent> * </complexType> * </pre> */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "sayHelloResponse", propOrder = { "wh", "hf" }) public class SayHelloResponse { protected String wh; protected String hf; /** * Gets the value of the wh property. * @return * possible object is * {@link String } */ public String getWh() { return wh; } /** * Sets the value of the wh property. * @param value * allowed object is * {@link String } */ public void setWh(String value) { this.wh = value; } /** * Gets the value of the hf property. * @return * possible object is * {@link String } */ public String getHf() { return hf; } /** * Sets the value of the hf property. * @param value * allowed object is * {@link String } */ public void setHf(String value) { this.hf = value; } }
ObjectFactory.java与package-info.java省略。
UUIDHandler.java(注意代码中helem.setMustUnderstand(true)这句代码)
package fibC; import java.io.IOException; import java.util.Set; import java.util.UUID; import javax.xml.namespace.QName; import javax.xml.soap.SOAPConstants; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPHeaderElement; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; /** * SOAP处理程序(Handler) * @author FUHD */ public class UUIDHandler implements SOAPHandler<SOAPMessageContext> { @Override public boolean handleMessage(SOAPMessageContext context) { Boolean request = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if(request){ UUID uuid = UUID.randomUUID(); try { SOAPMessage msg = context.getMessage(); SOAPEnvelope env = msg.getSOAPPart().getEnvelope(); SOAPHeader hdr = env.getHeader(); if(hdr == null) hdr = env.addHeader(); QName qname = new QName("http://ts.ch03/","uuid"); SOAPHeaderElement helem = hdr.addHeaderElement(qname); helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT); helem.setMustUnderstand(true); helem.addTextNode(uuid.toString()); msg.saveChanges(); msg.writeTo(System.out); System.out.println(); } catch (SOAPException e1) { e1.printStackTrace(); } catch (IOException e2) { e2.printStackTrace(); } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { try { context.getMessage().writeTo(System.out); } catch (SOAPException e1) { e1.printStackTrace(); } catch (IOException e2) { e2.printStackTrace(); } return true; } @Override public void close(MessageContext context) { } @Override public Set<QName> getHeaders() { return null; } }
handler-chain.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class>fibC.UUIDHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
HelloWordClient.java
package hw5; import javax.xml.ws.Holder; public class HelloWordClient { public static void main(String[] args) { String name = "老板"; Holder<String> wh = new Holder<String>(); wh.value = "你好"; Holder<String> hf = new Holder<String>(); HelloWordImplService service = new HelloWordImplService(); HelloWord port = service.getPort(HelloWord.class); port.sayHello(name, wh, hf); System.out.println(hf.value + "," + wh.value); } }
如上例,我们只需要很少的几个步骤就可以将HelloWord服务对SOAP版本的支持由1.1版转换到1.2版。比较重要的一步就是在SIB增加注解@BindingType。
JWS还拥有一个针对标准的SOAP1.2绑定常量:
javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING
但是端点发布程序却不能够在此标准绑定版本下生成WSDL文档。变通方法就是使用非标准绑定,正如我们这个例子所演示的。当服务发布后,端点提示有下面的非致命警告:
五月 06, 2014 2:32:13 下午 com.sun.xml.internal.ws.server.EndpointFactory generateWSDL 警告: Generating non-standard WSDL for the specified binding
在前面,wsgen工具也是利用了此种方法,但是现在wsimport通过“-extension”标识指明该命令将以非标准的方式生成客户端代理程序。如上例中:
% wsimport -keep -extension -p hw5 http://localhost:7654/ts?wsdl
另外,我们在服务端使用了UUIDValidator这个Handler程序,因此修改UUIDvalidator,展示SOAP1.2的关键特点。通常在一个SOAP1.2报头中使用属性mustUnderstand。如上例,直接在setActor方法调用的后面:
helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT); helem.setMustUnderstand(true); //SOAP1.2
根据这个例子抓取它的一个请求消息内容:
<?xml version="1.0" ?> <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"> <S:Header> <uuid xmlns="http://ts.ch03/" xmlns:env="http://www.w3.org/2003/05/soap-envelope" env:mustUnderstand="true" env:role="http://schemas.xmlsoap.org/soap/actor/next"> 03d7d564-b1b1-457a-9129-069a82acb8d6 </uuid> </S:Header> <S:Body> <ns2:sayHello xmlns:ns2="http://ts.ch03/"> <name xmlns="">老板</name> <wh xmlns="">你好</wh> </ns2:sayHello> </S:Body> </S:Envelope>
从上面请求消息可以看到:env:nustUnderstand="true"已经存在于uuid的属性中。
另外,在端点程序生成的WSDL文档的绑定部分反映了SOAP1.1改为SOAP1.2后发生的变化:
<binding name="HelloWordImplPortBinding" type="tns:HelloWord"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap12:operation soapAction=""/> <input> <soap12:body use="literal"/> </input> <output> <soap12:body use="literal"/> </output> </operation> </binding>
考虑到SOAP1.1和SOAP1.2之间差别不大,以及SOAP1.1已经是事实上的标准,除非不得已采用SOAP1.2版外,持续支持SOAP1.1版本是非常有意义的。最后,SOAP1.2向后兼容SOAP1.1版。