CXF入门教程(5) -- webService异步调用模式
除了教程(3)中介绍的常见的同步调用模式,CXF还支持如下两种形式的异步调用模式:
下面是两种异步调用的方法的描述和示例代码。 异步调用示例使用的契约下面展示的是异步调用示例中使用的WSDL契约,为保证教程的连续性,本文使用的是前面教程(1)中生成的HelloWorld服务的WSDL契约。 <?xml version="1.0" ?> <wsdl:definitions name="HelloWorld" targetNamespace="http://service.server.cxf.test.neareast.com/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <xs:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://service.server.cxf.test.neareast.com/" xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="IntegerUserMap" type="tns:IntegerUserMap"></xs:element> <xs:complexType name="User"> <xs:sequence> <xs:element minOccurs="0" name="name" type="xs:string"></xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="IntegerUserMap"> <xs:sequence> <xs:element maxOccurs="unbounded" minOccurs="0" name="entry" type="tns:IdentifiedUser"></xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="IdentifiedUser"> <xs:sequence> <xs:element name="id" type="xs:int"></xs:element> <xs:element minOccurs="0" name="user" type="tns:User"></xs:element> </xs:sequence> </xs:complexType> <xs:element name="sayHi" type="tns:sayHi"></xs:element> <xs:complexType name="sayHi"> <xs:sequence> <xs:element minOccurs="0" name="text" type="xs:string"></xs:element> </xs:sequence> </xs:complexType> <xs:element name="sayHiResponse" type="tns:sayHiResponse"></xs:element> <xs:complexType name="sayHiResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="xs:string"></xs:element> </xs:sequence> </xs:complexType> <xs:element name="sayHiToUser" type="tns:sayHiToUser"></xs:element> <xs:complexType name="sayHiToUser"> <xs:sequence> <xs:element minOccurs="0" name="arg0" type="tns:User"></xs:element> </xs:sequence> </xs:complexType> <xs:element name="sayHiToUserResponse" type="tns:sayHiToUserResponse"></xs:element> <xs:complexType name="sayHiToUserResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="xs:string"></xs:element> </xs:sequence> </xs:complexType> <xs:element name="getUsers" type="tns:getUsers"></xs:element> <xs:complexType name="getUsers"> <xs:sequence></xs:sequence> </xs:complexType> <xs:element name="getUsersResponse" type="tns:getUsersResponse"></xs:element> <xs:complexType name="getUsersResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="tns:IntegerUserMap"></xs:element> </xs:sequence> </xs:complexType> </xs:schema> </wsdl:types> <wsdl:message name="getUsers"> <wsdl:part element="tns:getUsers" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="sayHi"> <wsdl:part element="tns:sayHi" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="sayHiToUser"> <wsdl:part element="tns:sayHiToUser" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="sayHiToUserResponse"> <wsdl:part element="tns:sayHiToUserResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="sayHiResponse"> <wsdl:part element="tns:sayHiResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="getUsersResponse"> <wsdl:part element="tns:getUsersResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:portType name="iHelloWorld"> <wsdl:operation name="sayHi"> <wsdl:input message="tns:sayHi" name="sayHi"> </wsdl:input> <wsdl:output message="tns:sayHiResponse" name="sayHiResponse"> </wsdl:output> </wsdl:operation> <wsdl:operation name="sayHiToUser"> <wsdl:input message="tns:sayHiToUser" name="sayHiToUser"> </wsdl:input> <wsdl:output message="tns:sayHiToUserResponse" name="sayHiToUserResponse"> </wsdl:output> </wsdl:operation> <wsdl:operation name="getUsers"> <wsdl:input message="tns:getUsers" name="getUsers"> </wsdl:input> <wsdl:output message="tns:getUsersResponse" name="getUsersResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="HelloWorldSoapBinding" type="tns:iHelloWorld"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding> <wsdl:operation name="sayHi"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="sayHi"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="sayHiResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> <wsdl:operation name="sayHiToUser"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="sayHiToUser"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="sayHiToUserResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> <wsdl:operation name="getUsers"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="getUsers"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="getUsersResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="HelloWorld"> <wsdl:port binding="tns:HelloWorldSoapBinding" name="HelloWorldImplPort"> <soap:address location="http://localhost:9000/helloWorld"></soap:address> </wsdl:port> </wsdl:service> </wsdl:definitions> 生成异步 stub 代码异步调用需要额外的stub代码(例如,服务端点接口中定义的专用的异步方法)。然而,这些特殊的stub代码不是默认生成的。要想打开异步特性,并生成必不可少的stub代码,我们必须使用WSDL 2.0规范的自定义映射特性。 自定义使我们能够改变 wsdl2java 工具生成stub代码的方式。特别地,它允许我们修改WSDL到Java的映射,并打开某些特性。在这里,自定义的作用是打开异步调用特性。自定义是用一个绑定声明规定的,该声明是我们用一个?jaxws:bindings 标签(jaxws?前缀绑定到?http://java.sun.com/xml/ns/jaxws?命名空间)定义的。指定一个绑定声明有两种可选的方式:
本文只考虑第一种方法,即外部绑定声明。一个打开了异步调用开关的绑定声明文件的模版如下所示: <bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" wsdlLocation="http://localhost:9000/helloWorld?wsdl" xmlns="http://java.sun.com/xml/ns/jaxws"> <bindings node="wsdl:definitions"> <enableAsyncMapping>true</enableAsyncMapping> </bindings> </bindings> 其中的wsdlLocation指定了该绑定声明影响的WSDL文件的位置,可以是本地文件或一个URL。node节点是一个XPath 值,指定该绑定声明影响所影响的WSDL契约中的节点。 此处把node设为“wsdl:definitions”,表示我们希望对整个WSDL契约起作用。{jaxws:enableAsyncMapping}} 元素设置为true,用来使能异步调用特性。 如果我们只想对一个端口“iHelloWorld”生成异步方法,我们可以在前面的绑定声明中指定<bindings node="wsdl:definitions/wsdl:portType[@name='iHelloWorld']">?。 接下来我们就可以使用wsdl2java命令来生成相应的带异步支持的stub代码了。为简单起见,假设绑定声明文件存储在本地文件async_binding.xml中,我们可以使用类似下面的命令: wsdl2java -b async_binding.xml hello_world.wsdl 其中-b 选项用来指定绑定声明文件。通过这种方法生成stub代码之后,HelloWorld的服务端点接口定义如下: import java.util.concurrent.Future; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.ws.AsyncHandler; import javax.xml.ws.RequestWrapper; import javax.xml.ws.Response; import javax.xml.ws.ResponseWrapper; @WebService(targetNamespace = "http://service.server.cxf.test.neareast.com/",name = "iHelloWorld") @XmlSeeAlso({ObjectFactory.class}) public interface IHelloWorld { @RequestWrapper(localName = "sayHi",targetNamespace = "http://service.server.cxf.test.neareast.com/",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi") @ResponseWrapper(localName = "sayHiResponse",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse") @WebMethod(operationName = "sayHi") public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> sayHiAsync( @WebParam(name = "text",targetNamespace = "") java.lang.String text ); @RequestWrapper(localName = "sayHi",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse") @WebMethod(operationName = "sayHi") public Future<?> sayHiAsync( @WebParam(name = "text",targetNamespace = "") java.lang.String text,@WebParam(name = "asyncHandler",targetNamespace = "") AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> asyncHandler ); @WebResult(name = "return",targetNamespace = "") @RequestWrapper(localName = "sayHi",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi") @WebMethod @ResponseWrapper(localName = "sayHiResponse",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse") public java.lang.String sayHi( @WebParam(name = "text",targetNamespace = "") java.lang.String text ); @RequestWrapper(localName = "sayHiToUser",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser") @ResponseWrapper(localName = "sayHiToUserResponse",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse") @WebMethod(operationName = "sayHiToUser") public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> sayHiToUserAsync( @WebParam(name = "arg0",targetNamespace = "") com.neareast.test.cxf.asyClient.WSDL2Java.User arg0 ); @RequestWrapper(localName = "sayHiToUser",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse") @WebMethod(operationName = "sayHiToUser") public Future<?> sayHiToUserAsync( @WebParam(name = "arg0",targetNamespace = "") com.neareast.test.cxf.asyClient.WSDL2Java.User arg0,targetNamespace = "") AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> asyncHandler ); @WebResult(name = "return",targetNamespace = "") @RequestWrapper(localName = "sayHiToUser",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser") @WebMethod @ResponseWrapper(localName = "sayHiToUserResponse",className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse") public java.lang.String sayHiToUser( @WebParam(name = "arg0",targetNamespace = "") com.neareast.test.cxf.asyClient.WSDL2Java.User arg0 ); @RequestWrapper(localName = "getUsers",className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers") @ResponseWrapper(localName = "getUsersResponse",className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse") @WebMethod(operationName = "getUsers") public Response<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> getUsersAsync(); @RequestWrapper(localName = "getUsers",className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse") @WebMethod(operationName = "getUsers") public Future<?> getUsersAsync( @WebParam(name = "asyncHandler",targetNamespace = "") AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> asyncHandler ); @WebResult(name = "return",targetNamespace = "") @RequestWrapper(localName = "getUsers",className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers") @WebMethod @ResponseWrapper(localName = "getUsersResponse",className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse") public com.neareast.test.cxf.asyClient.WSDL2Java.IntegerUserMap getUsers(); } 除了原来的同步方法(如sayHi方法),sayHi操作的两个异步调用方法也被同时生成了:
回调方式和轮询方式的细节将在下面的章节讨论。为体现异步调用的特点,笔者修改了教程(1)中Helloworld服务的部分实现,在sayHiToUser()方法中加入了3秒钟的休眠,并增强了代码的鲁棒性,改动如下: public String sayHiToUser(User user) { String retVal = null; if(null == user){ retVal = "Error: user object null !"; }else{ try{ System.out.println("sleep for 3 seconds before return"); Thread.sleep(3000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("sayHiToUser called by: " + user.getName()); users.put(users.size() + 1,user); retVal = "Hello " + user.getName(); } return retVal; } 实现一个轮询方式的异步调用客户端下面的代码演示了异步发送操作调用的轮询方式的实现。客户端是通过特殊的Java方法?_OperationName_Async(本例为sayHiAsync()方法)来调用这个操作的,该方法返回一个javax.xml.ws.Response<T> 对象,其中“T”是这个操作的响应消息的类型(本例中为SayHiResponse类型)。我们可以稍后通过轮询Response<T> 对象来检查该操作的响应消息是否已经到达。? package com.neareast.test.cxf.asyClient.consumer; import java.util.concurrent.ExecutionException; import javax.xml.ws.Response; import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld; import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld; import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse; public class BasicClientPolling { public static void main(String[] args) throws InterruptedException{ HelloWorld server = new HelloWorld(); IHelloWorld hello = server.getHelloWorldImplPort(); Response<SayHiResponse> sayHiResponseResp = hello.sayHiAsync(System.getProperty("user.name")); while (!sayHiResponseResp.isDone()) { Thread.sleep(100); } try { SayHiResponse reply = sayHiResponseResp.get(); System.out.println( reply.getReturn() ); } catch (ExecutionException e) { e.printStackTrace(); } } } sayHiAsync()方法调用了sayHi操作,将输入参数传送到远程的服务,并返回javax.xml.ws.Response<SayHiResponse>?对象的一个引用。Response?类实现了标准的?java.util.concurrency.Future<T>?接口,该类设计用来轮询一个并发线程执行的任务的产出结果。本质上来说,使用Response对象来轮询有两种基本方法:
实现一个回调方式的异步调用客户端发起异步操作调用的另一个可选方法是实现javax.xml.ws.AsyncHandler接口,派生出一个回调类。回调类必须实现?handleResponse()?方法,CXF运行时调用这个类将响应的到达通知给客户端。下面的代码给出了我们需要实现的?AsyncHandler?接口的轮廓。
The javax.xml.ws.AsyncHandler Interface
package javax.xml.ws; public interface AsyncHandler<T> { void handleResponse(Response<T> res); } 本例使用一个测试用的回调类?SayHiToUserAsyHandler,代码如下: package com.neareast.test.cxf.asyClient.consumer; import java.util.concurrent.ExecutionException; import javax.xml.ws.AsyncHandler; import javax.xml.ws.Response; import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse; public class SayHiToUserAsyHandler implements AsyncHandler<SayHiToUserResponse> { SayHiToUserResponse reply = null; @Override public void handleResponse(Response<SayHiToUserResponse> res) { try { reply = res.get(); System.out.println( reply.getReturn() ); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } public String getResponseText(){ return reply.getReturn(); } } 上述?handleResponse()?的实现只是简单地获取响应数据,并把它存放到成员变量reply中。额外的getResponseText()?方法是为了方便地从响应中提炼出主要的输出参数。 下面的代码演示了发起异步操作调用的回调方法。客户端通过特定的Java方法?_OperationName_Async()来调用相应的操作,该方法使用一个额外的AsyncHandler<T>类型的参数,并返回一个?java.util.concurrency.Future<?>?对象。 package com.neareast.test.cxf.asyClient.consumer; import java.util.concurrent.Future; import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld; import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld; import com.neareast.test.cxf.asyClient.WSDL2Java.User; public class BasicCallbackClient { public static void main(String[] args) throws InterruptedException{ HelloWorld server = new HelloWorld(); IHelloWorld hello = server.getHelloWorldImplPort(); User u = new User(); //非阻塞式轮询 u.setName(System.getProperty("user.name")); SayHiToUserAsyHandler asyHandler = new SayHiToUserAsyHandler(); Future<?> res = hello.sayHiToUserAsync(u,asyHandler); while (!res.isDone()) { Thread.sleep(100); } String reply = asyHandler.getResponseText(); System.out.println( reply ); } } sayHiToUserAsync()方法返回的?Future<?>?对象只是用来检测一个响应是否已经到达的 —— 例如,通过调用response.isDone()来轮询。响应消息的值只在回调对象SayHiToUserAsyHandler 中可得。? 本文配套的完整代码已经上传,包括用到的wsdl契约文件和绑定声明文件;本文涉及的异步调用客户端的代码放在com.neareast.test.cxf.asyClient包下,欢迎下载:http://download.csdn.net/detail/neareast/4421250。 本文参考自:http://cxf.apache.org/docs/developing-a-consumer.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |