还是以前面的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版。