前面博客中,我们通过WSDL创建的客户端几乎都是同步的,也就是从web服务端有响应返回或有一个异常抛出之前,调用将一直阻塞。另外JWS也支持客户端对web服务的非阻塞或异步方式调用(注:好像RPC样式的不支持异步调用)。
以前面的HelloWord为例,我们调用下面的命令:
% wsimport -s source -p hw3 http://localhost:7654/ts?wsdl -b custom.xml
为了创建非阻塞的客户端(或者异步的客户端),这里我们使用了定制化的绑定文件:custom.xml。custom.xml中的内容如下:
<jaxws:bindings wsdlLocation = "http://localhost:7654/ts?wsdl" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"> <jaxws:enableAsyncMapping>true</jaxws:enableAsyncMapping> </jaxws:bindings>
文档中将enableAsyncMapping属性值设置为true。这样生成的客户端代码如下:
HelloWord.java:
package hw3; import java.util.concurrent.Future; 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.AsyncHandler; import javax.xml.ws.Holder; import javax.xml.ws.RequestWrapper; import javax.xml.ws.Response; 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 * @return * returns javax.xml.ws.Response<hw3.SayHelloResponse> */ @WebMethod(operationName = "sayHello") @RequestWrapper(localName = "sayHello", targetNamespace = "http://ts.ch03/", className = "hw3.SayHello") @ResponseWrapper(localName = "sayHelloResponse", targetNamespace = "http://ts.ch03/", className = "hw3.SayHelloResponse") public Response<SayHelloResponse> sayHelloAsync( @WebParam(name = "name", targetNamespace = "") String name, @WebParam(name = "wh", targetNamespace = "") String wh); /** * @param wh * @param name * @param asyncHandler * @return * returns java.util.concurrent.Future<? extends java.lang.Object> */ @WebMethod(operationName = "sayHello") @RequestWrapper(localName = "sayHello", targetNamespace = "http://ts.ch03/", className = "hw3.SayHello") @ResponseWrapper(localName = "sayHelloResponse", targetNamespace = "http://ts.ch03/", className = "hw3.SayHelloResponse") public Future<?> sayHelloAsync( @WebParam(name = "name", targetNamespace = "") String name, @WebParam(name = "wh", targetNamespace = "") String wh, @WebParam(name = "asyncHandler", targetNamespace = "") AsyncHandler<SayHelloResponse> asyncHandler); /** * @param wh * @param name * @param hf */ @WebMethod @RequestWrapper(localName = "sayHello", targetNamespace = "http://ts.ch03/", className = "hw3.SayHello") @ResponseWrapper(localName = "sayHelloResponse", targetNamespace = "http://ts.ch03/", className = "hw3.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 hw3; import java.net.MalformedURLException; import java.net.URL; 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") 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 hw3; 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 hw3; 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略。
异步调用可以采用不同方式。第一种方式的客户端异步调用示例如下:
package hw3; import java.util.concurrent.ExecutionException; import hw3.HelloWord; import hw3.HelloWordImplService; import javax.xml.ws.Response; public class HelloWordClient3 { public static void main(String[] args) { String name = "老师"; String wh = "你好"; HelloWordImplService service = new HelloWordImplService(); HelloWord port = service.getPort(HelloWord.class); Response<SayHelloResponse> resp = port.sayHelloAsync(name, wh); //响应是否完成 while(!resp.isDone()){ System.out.println("is not Done!"); } try { SayHelloResponse response = resp.get(); String hf1 = response.getHf(); String wh1 = response.getWh(); System.out.println(hf1 + "!" + wh1); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
这个示例是以轮询的方式(polling)实现异步调用。
第二个异步调用方式示例如下:
package hw3; import java.util.concurrent.ExecutionException; import javax.xml.ws.AsyncHandler; import javax.xml.ws.Response; public class HelloWordClient3 { public static void main(String[] args) { String name = "老师"; String wh = "你好"; HelloWordImplService service = new HelloWordImplService(); HelloWord port = service.getPort(HelloWord.class); //这里使用了一个匿名内部类 port.sayHelloAsync(name, wh, new AsyncHandler<SayHelloResponse>(){ public void handleResponse(Response<SayHelloResponse> future){ try { SayHelloResponse response = future.get(); String hf1 = response.getHf(); String wh1 = response.getWh(); System.out.println(hf1 + "!" + wh1); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }); System.out.println("测试一下在之前还是在之后打印!!!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
这个示例是以回调的方式(callback)来演示异步调用的。由于此callback当请求发出去以后,当前的这个连接就会关闭,为了达到测试目的,加入 sleep,让客户端程序等待服务端的返回。注意,这个方法要传入一个javax.xml.ws.AsyncHandler类型的对象,所以这里我们定义了一个匿名内部类。当SOAP响应消息到达时,JAXWS会调用handleResponse这个方法来处理response。