cxf webService异步调用模式


    • 轮询方法(Polling approach) - 这种情况下调用远程方法,我们可以调用一个特殊的方法;该方法没有输出参数,但是返回一个 javax.xml.ws.Response 实例。可以轮询该 Response 对象(继承自 javax.util.concurrency.Future 接口)来检查是否有应答消息到达。
    • 回调方法(Callback approach) -这种情况下调用远程方法,我们调用另外一个特殊的方法:该方法使用一个回调对象(javax.xml.ws.AsyncHandler类型)的引用作为一个参数。只要有应答消息到达客户端,CXF运行时就会回调该 AsyncHandler 对象,并将应答消息的内容传给它。

    下面是两种异步调用的方法的描述和示例代码。

    异步调用示例使用的契约

    下面展示的是异步调用示例中使用的WSDL契约,为保证教程的连续性,本文使用的是前面教程(1)中生成的HelloWorld服务的WSDL契约

    [java]  view plain  copy
    1. <?xml version="1.0" ?>  
    2. <wsdl:definitions name="HelloWorld"  
    3.     targetNamespace="http://service.server.cxf.test.neareast.com/"  
    4.     xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"  
    5.     xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  
    6.     xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    7.     <wsdl:types>  
    8.         <xs:schema attributeFormDefault="unqualified"  
    9.             elementFormDefault="unqualified" targetNamespace="http://service.server.cxf.test.neareast.com/"  
    10.             xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema">  
    11.             <xs:element name="IntegerUserMap" type="tns:IntegerUserMap"></xs:element>  
    12.             <xs:complexType name="User">  
    13.                 <xs:sequence>  
    14.                     <xs:element minOccurs="0" name="name" type="xs:string"></xs:element>  
    15.                 </xs:sequence>  
    16.             </xs:complexType>  
    17.             <xs:complexType name="IntegerUserMap">  
    18.                 <xs:sequence>  
    19.                     <xs:element maxOccurs="unbounded" minOccurs="0" name="entry"  
    20.                         type="tns:IdentifiedUser"></xs:element>  
    21.                 </xs:sequence>  
    22.             </xs:complexType>  
    23.             <xs:complexType name="IdentifiedUser">  
    24.                 <xs:sequence>  
    25.                     <xs:element name="id" type="xs:int"></xs:element>  
    26.                     <xs:element minOccurs="0" name="user" type="tns:User"></xs:element>  
    27.                 </xs:sequence>  
    28.             </xs:complexType>  
    29.             <xs:element name="sayHi" type="tns:sayHi"></xs:element>  
    30.             <xs:complexType name="sayHi">  
    31.                 <xs:sequence>  
    32.                     <xs:element minOccurs="0" name="text" type="xs:string"></xs:element>  
    33.                 </xs:sequence>  
    34.             </xs:complexType>  
    35.             <xs:element name="sayHiResponse" type="tns:sayHiResponse"></xs:element>  
    36.             <xs:complexType name="sayHiResponse">  
    37.                 <xs:sequence>  
    38.                     <xs:element minOccurs="0" name="return" type="xs:string"></xs:element>  
    39.                 </xs:sequence>  
    40.             </xs:complexType>  
    41.             <xs:element name="sayHiToUser" type="tns:sayHiToUser"></xs:element>  
    42.             <xs:complexType name="sayHiToUser">  
    43.                 <xs:sequence>  
    44.                     <xs:element minOccurs="0" name="arg0" type="tns:User"></xs:element>  
    45.                 </xs:sequence>  
    46.             </xs:complexType>  
    47.             <xs:element name="sayHiToUserResponse" type="tns:sayHiToUserResponse"></xs:element>  
    48.             <xs:complexType name="sayHiToUserResponse">  
    49.                 <xs:sequence>  
    50.                     <xs:element minOccurs="0" name="return" type="xs:string"></xs:element>  
    51.                 </xs:sequence>  
    52.             </xs:complexType>  
    53.             <xs:element name="getUsers" type="tns:getUsers"></xs:element>  
    54.             <xs:complexType name="getUsers">  
    55.                 <xs:sequence></xs:sequence>  
    56.             </xs:complexType>  
    57.             <xs:element name="getUsersResponse" type="tns:getUsersResponse"></xs:element>  
    58.             <xs:complexType name="getUsersResponse">  
    59.                 <xs:sequence>  
    60.                     <xs:element minOccurs="0" name="return" type="tns:IntegerUserMap"></xs:element>  
    61.                 </xs:sequence>  
    62.             </xs:complexType>  
    63.         </xs:schema>  
    64.     </wsdl:types>  
    65.     <wsdl:message name="getUsers">  
    66.         <wsdl:part element="tns:getUsers" name="parameters">  
    67.         </wsdl:part>  
    68.     </wsdl:message>  
    69.     <wsdl:message name="sayHi">  
    70.         <wsdl:part element="tns:sayHi" name="parameters">  
    71.         </wsdl:part>  
    72.     </wsdl:message>  
    73.     <wsdl:message name="sayHiToUser">  
    74.         <wsdl:part element="tns:sayHiToUser" name="parameters">  
    75.         </wsdl:part>  
    76.     </wsdl:message>  
    77.     <wsdl:message name="sayHiToUserResponse">  
    78.         <wsdl:part element="tns:sayHiToUserResponse" name="parameters">  
    79.         </wsdl:part>  
    80.     </wsdl:message>  
    81.     <wsdl:message name="sayHiResponse">  
    82.         <wsdl:part element="tns:sayHiResponse" name="parameters">  
    83.         </wsdl:part>  
    84.     </wsdl:message>  
    85.     <wsdl:message name="getUsersResponse">  
    86.         <wsdl:part element="tns:getUsersResponse" name="parameters">  
    87.         </wsdl:part>  
    88.     </wsdl:message>  
    89.     <wsdl:portType name="iHelloWorld">  
    90.         <wsdl:operation name="sayHi">  
    91.             <wsdl:input message="tns:sayHi" name="sayHi">  
    92.             </wsdl:input>  
    93.             <wsdl:output message="tns:sayHiResponse" name="sayHiResponse">  
    94.             </wsdl:output>  
    95.         </wsdl:operation>  
    96.         <wsdl:operation name="sayHiToUser">  
    97.             <wsdl:input message="tns:sayHiToUser" name="sayHiToUser">  
    98.             </wsdl:input>  
    99.             <wsdl:output message="tns:sayHiToUserResponse" name="sayHiToUserResponse">  
    100.             </wsdl:output>  
    101.         </wsdl:operation>  
    102.         <wsdl:operation name="getUsers">  
    103.             <wsdl:input message="tns:getUsers" name="getUsers">  
    104.             </wsdl:input>  
    105.             <wsdl:output message="tns:getUsersResponse" name="getUsersResponse">  
    106.             </wsdl:output>  
    107.         </wsdl:operation>  
    108.     </wsdl:portType>  
    109.     <wsdl:binding name="HelloWorldSoapBinding" type="tns:iHelloWorld">  
    110.         <soap:binding style="document"  
    111.             transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>  
    112.         <wsdl:operation name="sayHi">  
    113.             <soap:operation soapAction="" style="document"></soap:operation>  
    114.             <wsdl:input name="sayHi">  
    115.                 <soap:body use="literal"></soap:body>  
    116.             </wsdl:input>  
    117.             <wsdl:output name="sayHiResponse">  
    118.                 <soap:body use="literal"></soap:body>  
    119.             </wsdl:output>  
    120.         </wsdl:operation>  
    121.         <wsdl:operation name="sayHiToUser">  
    122.             <soap:operation soapAction="" style="document"></soap:operation>  
    123.             <wsdl:input name="sayHiToUser">  
    124.                 <soap:body use="literal"></soap:body>  
    125.             </wsdl:input>  
    126.             <wsdl:output name="sayHiToUserResponse">  
    127.                 <soap:body use="literal"></soap:body>  
    128.             </wsdl:output>  
    129.         </wsdl:operation>  
    130.         <wsdl:operation name="getUsers">  
    131.             <soap:operation soapAction="" style="document"></soap:operation>  
    132.             <wsdl:input name="getUsers">  
    133.                 <soap:body use="literal"></soap:body>  
    134.             </wsdl:input>  
    135.             <wsdl:output name="getUsersResponse">  
    136.                 <soap:body use="literal"></soap:body>  
    137.             </wsdl:output>  
    138.         </wsdl:operation>  
    139.     </wsdl:binding>  
    140.     <wsdl:service name="HelloWorld">  
    141.         <wsdl:port binding="tns:HelloWorldSoapBinding" name="HelloWorldImplPort">  
    142.             <soap:address location="http://localhost:9000/helloWorld"></soap:address>  
    143.         </wsdl:port>  
    144.     </wsdl:service>  
    145. </wsdl:definitions>  

    生成异步 stub 代码

    异步调用需要额外的stub代码(例如,服务端点接口中定义的专用的异步方法)。然而,这些特殊的stub代码不是默认生成的。要想打开异步特性,并生成必不可少的stub代码,我们必须使用WSDL 2.0规范的自定义映射特性。

    自定义使我们能够改变 wsdl2java 工具生成stub代码的方式。特别地,它允许我们修改WSDL到Java的映射,并打开某些特性。在这里,自定义的作用是打开异步调用特性。自定义是用一个绑定声明规定的,该声明是我们用一个 jaxws:bindings 标签(jaxws 前缀绑定到 http://java.sun.com/xml/ns/jaxws 命名空间)定义的。指定一个绑定声明有两种可选的方式:

    • 外部绑定声明 - jaxws:bindings 元素被定义在WSDL契约之外的一个单独的文件。生成stub代码的时候,我们需要对wsdl2java 工具指定绑定声明文件的位置。
    • 嵌入式绑定声明 - 我们也可以直接把jaxws:bindings 元素嵌入到 WSDL 契约中,把它当做WSDL的扩展。在这种情况下,jaxws:bindings 的设置仅对直接的父元素起作用。

    本文只考虑第一种方法,即外部绑定声明。一个打开了异步调用开关的绑定声明文件的模版如下所示:

    [java]  view plain  copy
    1. <bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
    2.           xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  
    3.           wsdlLocation="http://localhost:9000/helloWorld?wsdl"  
    4.           xmlns="http://java.sun.com/xml/ns/jaxws">  
    5.   <bindings node="wsdl:definitions">  
    6.     <enableAsyncMapping>true</enableAsyncMapping>  
    7.   </bindings>  
    8. </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 -p hellworldws http://localhost:8080/jdbc/ws/tranws?wsdl

    其中-b 选项用来指定绑定声明文件。通过这种方法生成stub代码之后,HelloWorld的服务端点接口定义如下:

    [java]  view plain  copy
    1. import java.util.concurrent.Future;  
    2. import javax.jws.WebMethod;  
    3. import javax.jws.WebParam;  
    4. import javax.jws.WebResult;  
    5. import javax.jws.WebService;  
    6. import javax.xml.bind.annotation.XmlSeeAlso;  
    7. import javax.xml.ws.AsyncHandler;  
    8. import javax.xml.ws.RequestWrapper;  
    9. import javax.xml.ws.Response;  
    10. import javax.xml.ws.ResponseWrapper;  
    11.   
    12.   
    13. @WebService(targetNamespace = "http://service.server.cxf.test.neareast.com/", name = "iHelloWorld")  
    14. @XmlSeeAlso({ObjectFactory.class})  
    15. public interface IHelloWorld {  
    16.   
    17.     @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")  
    18.     @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")  
    19.     @WebMethod(operationName = "sayHi")  
    20.     public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> sayHiAsync(  
    21.         @WebParam(name = "text", targetNamespace = "")  
    22.         java.lang.String text  
    23.     );  
    24.   
    25.     @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")  
    26.     @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")  
    27.     @WebMethod(operationName = "sayHi")  
    28.     public Future<?> sayHiAsync(  
    29.         @WebParam(name = "text", targetNamespace = "")  
    30.         java.lang.String text,  
    31.         @WebParam(name = "asyncHandler", targetNamespace = "")  
    32.         AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> asyncHandler  
    33.     );  
    34.   
    35.     @WebResult(name = "return", targetNamespace = "")  
    36.     @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")  
    37.     @WebMethod  
    38.     @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")  
    39.     public java.lang.String sayHi(  
    40.         @WebParam(name = "text", targetNamespace = "")  
    41.         java.lang.String text  
    42.     );  
    43.   
    44.     @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")  
    45.     @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")  
    46.     @WebMethod(operationName = "sayHiToUser")  
    47.     public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> sayHiToUserAsync(  
    48.         @WebParam(name = "arg0", targetNamespace = "")  
    49.         com.neareast.test.cxf.asyClient.WSDL2Java.User arg0  
    50.     );  
    51.   
    52.     @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")  
    53.     @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")  
    54.     @WebMethod(operationName = "sayHiToUser")  
    55.     public Future<?> sayHiToUserAsync(  
    56.         @WebParam(name = "arg0", targetNamespace = "")  
    57.         com.neareast.test.cxf.asyClient.WSDL2Java.User arg0,  
    58.         @WebParam(name = "asyncHandler", targetNamespace = "")  
    59.         AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> asyncHandler  
    60.     );  
    61.   
    62.     @WebResult(name = "return", targetNamespace = "")  
    63.     @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")  
    64.     @WebMethod  
    65.     @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")  
    66.     public java.lang.String sayHiToUser(  
    67.         @WebParam(name = "arg0", targetNamespace = "")  
    68.         com.neareast.test.cxf.asyClient.WSDL2Java.User arg0  
    69.     );  
    70.   
    71.     @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")  
    72.     @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")  
    73.     @WebMethod(operationName = "getUsers")  
    74.     public Response<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> getUsersAsync();  
    75.   
    76.     @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")  
    77.     @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")  
    78.     @WebMethod(operationName = "getUsers")  
    79.     public Future<?> getUsersAsync(  
    80.         @WebParam(name = "asyncHandler", targetNamespace = "")  
    81.         AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> asyncHandler  
    82.     );  
    83.   
    84.     @WebResult(name = "return", targetNamespace = "")  
    85.     @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")  
    86.     @WebMethod  
    87.     @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")  
    88.     public com.neareast.test.cxf.asyClient.WSDL2Java.IntegerUserMap getUsers();  
    89. }  

    除了原来的同步方法(如sayHi方法),sayHi操作的两个异步调用方法也被同时生成了:

    • 返回值类型为Future<?>,有一个类型为javax.xml.ws.AsyncHandler的额外参数的sayHiAsync()方法 —— 该方法可用于异步调用的回调方式。
    • 返回值类型为Response<GreetMeSometimeResponse>的sayHiAsync()方法 —— 该方法可用于异步调用的轮询方式。

    回调方式和轮询方式的细节将在下面的章节讨论。为体现异步调用的特点,笔者修改了教程(1)中Helloworld服务的部分实现,在sayHiToUser()方法中加入了3秒钟的休眠,并增强了代码的鲁棒性,改动如下:

    [java]  view plain  copy
    1. public String sayHiToUser(User user) {  
    2.     String retVal = null;  
    3.     if(null == user){  
    4.         retVal = "Error: user object null !";  
    5.     }else{  
    6.         try{  
    7.             System.out.println("sleep for 3 seconds before return");  
    8.             Thread.sleep(3000);  
    9.         }catch(InterruptedException e){  
    10.             e.printStackTrace();  
    11.         }  
    12.           
    13.         System.out.println("sayHiToUser called by: " + user.getName());  
    14.         users.put(users.size() + 1, user);  
    15.         retVal = "Hello " + user.getName();  
    16.     }  
    17.     return retVal;  
    18. }  

    实现一个轮询方式的异步调用客户端

    下面的代码演示了异步发送操作调用的轮询方式的实现。客户端是通过特殊的Java方法 _OperationName_Async(本例为sayHiAsync()方法)来调用这个操作的,该方法返回一个javax.xml.ws.Response<T> 对象,其中“T”是这个操作的响应消息的类型(本例中为SayHiResponse类型)。我们可以稍后通过轮询Response<T> 对象来检查该操作的响应消息是否已经到达。 

    [java]  view plain  copy
    1. package com.neareast.test.cxf.asyClient.consumer;  
    2.   
    3. import java.util.concurrent.ExecutionException;  
    4.   
    5. import javax.xml.ws.Response;  
    6.   
    7. import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld;  
    8. import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld;  
    9. import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse;  
    10.   
    11. public class BasicClientPolling {  
    12.     public static void main(String[] args) throws InterruptedException{  
    13.         HelloWorld server = new HelloWorld();  
    14.         IHelloWorld hello = server.getHelloWorldImplPort();  
    15.   
    16.         Response<SayHiResponse> sayHiResponseResp = hello.sayHiAsync(System.getProperty("user.name"));  
    17.         while (!sayHiResponseResp.isDone()) {  
    18.             Thread.sleep(100);  
    19.         }  
    20.   
    21.         try {  
    22.             SayHiResponse reply = sayHiResponseResp.get();  
    23.             System.out.println( reply.getReturn() );  
    24.         } catch (ExecutionException e) {  
    25.             e.printStackTrace();  
    26.         }  
    27.           
    28.     }  
    29. }  

    sayHiAsync()方法调用了sayHi操作,将输入参数传送到远程的服务,并返回javax.xml.ws.Response<SayHiResponse> 对象的一个引用。Response 类实现了标准的 java.util.concurrency.Future<T> 接口,该类设计用来轮询一个并发线程执行的任务的产出结果。本质上来说,使用Response对象来轮询有两种基本方法:

    • Non-blocking polling(非阻塞轮询) - 尝试获得结果之前,调用非阻塞方法Response<T>.isDone()来检查响应消息是否到达,例如:
      [java]  view plain  copy
      1. <pre name="code" class="java">    User u = new User();  
      2.           
      3.     //非阻塞式轮询  
      4.     u.setName(System.getProperty("user.name"));  
      5.     Response<SayHiToUserResponse> sayHiToUserResponseResp = hello.sayHiToUserAsync(u);  
      6.     while (!sayHiToUserResponseResp.isDone()) {  
      7.         Thread.sleep(100);  
      8.     }  
      9.           
      10.     try {  
      11.         //如果没有前面isDone的检测,此处就退化为阻塞式轮询  
      12.         SayHiToUserResponse reply = sayHiToUserResponseResp.get();  
      13.         System.out.println( reply.getReturn() );  
      14.     } catch (ExecutionException e) {  
      15.         e.printStackTrace();  
      16.     }</pre><br>  
      17. <pre></pre>  
      18. <pre></pre>  
      19. <pre></pre>  
      20. <pre></pre>  
      21. <pre></pre>  
      22. <pre></pre>  
      23. <pre></pre>  
    • Blocking polling(阻塞轮询) - 立即调用Response<T>.get(),阻塞至响应到达(可以指定一个超时时长作为可选项)。例如,轮询一个响应,超时时长为60s:
      [java]  view plain  copy
      1. //阻塞式轮询  
      2. u.setName("NearEast");  
      3. sayHiToUserResponseResp = hello.sayHiToUserAsync(u);  
      4. try {  
      5.     SayHiToUserResponse reply = sayHiToUserResponseResp.get(5L,java.util.concurrent.TimeUnit.SECONDS);  
      6.     System.out.println( reply.getReturn() );  
      7. catch (ExecutionException e) {  
      8.     e.printStackTrace();  
      9. catch (TimeoutException e) {  
      10.     e.printStackTrace();  
      11. }  

    实现一个回调方式的异步调用客户端

    发起异步操作调用的另一个可选方法是实现javax.xml.ws.AsyncHandler接口,派生出一个回调类。回调类必须实现 handleResponse() 方法,CXF运行时调用这个类将响应的到达通知给客户端。下面的代码给出了我们需要实现的 AsyncHandler 接口的轮廓。

    The javax.xml.ws.AsyncHandler Interface
    <span class="code-keyword" style="color: rgb(0, 0, 145); background-color: inherit; ">package</span> javax.xml.ws;
    
    <span class="code-keyword" style="color: rgb(0, 0, 145); background-color: inherit; ">public</span> <span class="code-keyword" style="color: rgb(0, 0, 145); background-color: inherit; ">interface</span> AsyncHandler<T>
    {
      void handleResponse(Response<T> res);
    }
    

    本例使用一个测试用的回调类 SayHiToUserAsyHandler,代码如下:

    [java]  view plain  copy
    1. package com.neareast.test.cxf.asyClient.consumer;  
    2.   
    3. import java.util.concurrent.ExecutionException;  
    4.   
    5. import javax.xml.ws.AsyncHandler;  
    6. import javax.xml.ws.Response;  
    7.   
    8. import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse;  
    9.   
    10. public class SayHiToUserAsyHandler implements AsyncHandler<SayHiToUserResponse> {  
    11.     SayHiToUserResponse reply = null;  
    12.   
    13.     @Override  
    14.     public void handleResponse(Response<SayHiToUserResponse> res) {  
    15.         try {  
    16.             reply = res.get();  
    17.             System.out.println( reply.getReturn() );  
    18.         } catch (ExecutionException e) {  
    19.             e.printStackTrace();  
    20.         } catch (InterruptedException e) {  
    21.             e.printStackTrace();  
    22.         }  
    23.     }  
    24.       
    25.     public String getResponseText(){  
    26.         return reply.getReturn();  
    27.     }  
    28.   
    29. }  

    上述 handleResponse() 的实现只是简单地获取响应数据,并把它存放到成员变量reply中。额外的getResponseText() 方法是为了方便地从响应中提炼出主要的输出参数。

    下面的代码演示了发起异步操作调用的回调方法。客户端通过特定的Java方法 _OperationName_Async()来调用相应的操作,该方法使用一个额外的AsyncHandler<T>类型的参数,并返回一个 java.util.concurrency.Future<?> 对象。

    [java]  view plain  copy
    1. package com.neareast.test.cxf.asyClient.consumer;  
    2.   
    3. import java.util.concurrent.Future;  
    4.   
    5. import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld;  
    6. import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld;  
    7. import com.neareast.test.cxf.asyClient.WSDL2Java.User;  
    8.   
    9. public class BasicCallbackClient {  
    10.     public static void main(String[] args) throws InterruptedException{  
    11.         HelloWorld server = new HelloWorld();  
    12.         IHelloWorld hello = server.getHelloWorldImplPort();  
    13.           
    14.         User u = new User();  
    15.         //非阻塞式轮询  
    16.         u.setName(System.getProperty("user.name"));  
    17.         SayHiToUserAsyHandler asyHandler = new SayHiToUserAsyHandler();  
    18.         Future<?> res = hello.sayHiToUserAsync(u, asyHandler);  
    19.         while (!res.isDone()) {  
    20.             Thread.sleep(100);  
    21.         }  
    22.           
    23.         String reply = asyHandler.getResponseText();  
    24.         System.out.println( reply );  
    25.           
    26.     }  
    27. }  

    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

    轮询方法(Polling approach) - 这种情况下调用远程方法,我们可以调用一个特殊的方法;该方法没有输出参数,但是返回一个 javax.xml.ws.Response 实例。可以轮询该 Response 对象(继承自 javax.util.concurrency.Future 接口)来检查是否有应答消息到达。
  • 回调方法(Callback approach) -这种情况下调用远程方法,我们调用另外一个特殊的方法:该方法使用一个回调对象(javax.xml.ws.AsyncHandler类型)的引用作为一个参数。只要有应答消息到达客户端,CXF运行时就会回调该 AsyncHandler 对象,并将应答消息的内容传给它。

下面是两种异步调用的方法的描述和示例代码。

异步调用示例使用的契约

下面展示的是异步调用示例中使用的WSDL契约,为保证教程的连续性,本文使用的是前面教程(1)中生成的HelloWorld服务的WSDL契约

[java]  view plain  copy
  1. <?xml version="1.0" ?>  
  2. <wsdl:definitions name="HelloWorld"  
  3.     targetNamespace="http://service.server.cxf.test.neareast.com/"  
  4.     xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"  
  5.     xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  
  6.     xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  7.     <wsdl:types>  
  8.         <xs:schema attributeFormDefault="unqualified"  
  9.             elementFormDefault="unqualified" targetNamespace="http://service.server.cxf.test.neareast.com/"  
  10.             xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema">  
  11.             <xs:element name="IntegerUserMap" type="tns:IntegerUserMap"></xs:element>  
  12.             <xs:complexType name="User">  
  13.                 <xs:sequence>  
  14.                     <xs:element minOccurs="0" name="name" type="xs:string"></xs:element>  
  15.                 </xs:sequence>  
  16.             </xs:complexType>  
  17.             <xs:complexType name="IntegerUserMap">  
  18.                 <xs:sequence>  
  19.                     <xs:element maxOccurs="unbounded" minOccurs="0" name="entry"  
  20.                         type="tns:IdentifiedUser"></xs:element>  
  21.                 </xs:sequence>  
  22.             </xs:complexType>  
  23.             <xs:complexType name="IdentifiedUser">  
  24.                 <xs:sequence>  
  25.                     <xs:element name="id" type="xs:int"></xs:element>  
  26.                     <xs:element minOccurs="0" name="user" type="tns:User"></xs:element>  
  27.                 </xs:sequence>  
  28.             </xs:complexType>  
  29.             <xs:element name="sayHi" type="tns:sayHi"></xs:element>  
  30.             <xs:complexType name="sayHi">  
  31.                 <xs:sequence>  
  32.                     <xs:element minOccurs="0" name="text" type="xs:string"></xs:element>  
  33.                 </xs:sequence>  
  34.             </xs:complexType>  
  35.             <xs:element name="sayHiResponse" type="tns:sayHiResponse"></xs:element>  
  36.             <xs:complexType name="sayHiResponse">  
  37.                 <xs:sequence>  
  38.                     <xs:element minOccurs="0" name="return" type="xs:string"></xs:element>  
  39.                 </xs:sequence>  
  40.             </xs:complexType>  
  41.             <xs:element name="sayHiToUser" type="tns:sayHiToUser"></xs:element>  
  42.             <xs:complexType name="sayHiToUser">  
  43.                 <xs:sequence>  
  44.                     <xs:element minOccurs="0" name="arg0" type="tns:User"></xs:element>  
  45.                 </xs:sequence>  
  46.             </xs:complexType>  
  47.             <xs:element name="sayHiToUserResponse" type="tns:sayHiToUserResponse"></xs:element>  
  48.             <xs:complexType name="sayHiToUserResponse">  
  49.                 <xs:sequence>  
  50.                     <xs:element minOccurs="0" name="return" type="xs:string"></xs:element>  
  51.                 </xs:sequence>  
  52.             </xs:complexType>  
  53.             <xs:element name="getUsers" type="tns:getUsers"></xs:element>  
  54.             <xs:complexType name="getUsers">  
  55.                 <xs:sequence></xs:sequence>  
  56.             </xs:complexType>  
  57.             <xs:element name="getUsersResponse" type="tns:getUsersResponse"></xs:element>  
  58.             <xs:complexType name="getUsersResponse">  
  59.                 <xs:sequence>  
  60.                     <xs:element minOccurs="0" name="return" type="tns:IntegerUserMap"></xs:element>  
  61.                 </xs:sequence>  
  62.             </xs:complexType>  
  63.         </xs:schema>  
  64.     </wsdl:types>  
  65.     <wsdl:message name="getUsers">  
  66.         <wsdl:part element="tns:getUsers" name="parameters">  
  67.         </wsdl:part>  
  68.     </wsdl:message>  
  69.     <wsdl:message name="sayHi">  
  70.         <wsdl:part element="tns:sayHi" name="parameters">  
  71.         </wsdl:part>  
  72.     </wsdl:message>  
  73.     <wsdl:message name="sayHiToUser">  
  74.         <wsdl:part element="tns:sayHiToUser" name="parameters">  
  75.         </wsdl:part>  
  76.     </wsdl:message>  
  77.     <wsdl:message name="sayHiToUserResponse">  
  78.         <wsdl:part element="tns:sayHiToUserResponse" name="parameters">  
  79.         </wsdl:part>  
  80.     </wsdl:message>  
  81.     <wsdl:message name="sayHiResponse">  
  82.         <wsdl:part element="tns:sayHiResponse" name="parameters">  
  83.         </wsdl:part>  
  84.     </wsdl:message>  
  85.     <wsdl:message name="getUsersResponse">  
  86.         <wsdl:part element="tns:getUsersResponse" name="parameters">  
  87.         </wsdl:part>  
  88.     </wsdl:message>  
  89.     <wsdl:portType name="iHelloWorld">  
  90.         <wsdl:operation name="sayHi">  
  91.             <wsdl:input message="tns:sayHi" name="sayHi">  
  92.             </wsdl:input>  
  93.             <wsdl:output message="tns:sayHiResponse" name="sayHiResponse">  
  94.             </wsdl:output>  
  95.         </wsdl:operation>  
  96.         <wsdl:operation name="sayHiToUser">  
  97.             <wsdl:input message="tns:sayHiToUser" name="sayHiToUser">  
  98.             </wsdl:input>  
  99.             <wsdl:output message="tns:sayHiToUserResponse" name="sayHiToUserResponse">  
  100.             </wsdl:output>  
  101.         </wsdl:operation>  
  102.         <wsdl:operation name="getUsers">  
  103.             <wsdl:input message="tns:getUsers" name="getUsers">  
  104.             </wsdl:input>  
  105.             <wsdl:output message="tns:getUsersResponse" name="getUsersResponse">  
  106.             </wsdl:output>  
  107.         </wsdl:operation>  
  108.     </wsdl:portType>  
  109.     <wsdl:binding name="HelloWorldSoapBinding" type="tns:iHelloWorld">  
  110.         <soap:binding style="document"  
  111.             transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>  
  112.         <wsdl:operation name="sayHi">  
  113.             <soap:operation soapAction="" style="document"></soap:operation>  
  114.             <wsdl:input name="sayHi">  
  115.                 <soap:body use="literal"></soap:body>  
  116.             </wsdl:input>  
  117.             <wsdl:output name="sayHiResponse">  
  118.                 <soap:body use="literal"></soap:body>  
  119.             </wsdl:output>  
  120.         </wsdl:operation>  
  121.         <wsdl:operation name="sayHiToUser">  
  122.             <soap:operation soapAction="" style="document"></soap:operation>  
  123.             <wsdl:input name="sayHiToUser">  
  124.                 <soap:body use="literal"></soap:body>  
  125.             </wsdl:input>  
  126.             <wsdl:output name="sayHiToUserResponse">  
  127.                 <soap:body use="literal"></soap:body>  
  128.             </wsdl:output>  
  129.         </wsdl:operation>  
  130.         <wsdl:operation name="getUsers">  
  131.             <soap:operation soapAction="" style="document"></soap:operation>  
  132.             <wsdl:input name="getUsers">  
  133.                 <soap:body use="literal"></soap:body>  
  134.             </wsdl:input>  
  135.             <wsdl:output name="getUsersResponse">  
  136.                 <soap:body use="literal"></soap:body>  
  137.             </wsdl:output>  
  138.         </wsdl:operation>  
  139.     </wsdl:binding>  
  140.     <wsdl:service name="HelloWorld">  
  141.         <wsdl:port binding="tns:HelloWorldSoapBinding" name="HelloWorldImplPort">  
  142.             <soap:address location="http://localhost:9000/helloWorld"></soap:address>  
  143.         </wsdl:port>  
  144.     </wsdl:service>  
  145. </wsdl:definitions>  

生成异步 stub 代码

异步调用需要额外的stub代码(例如,服务端点接口中定义的专用的异步方法)。然而,这些特殊的stub代码不是默认生成的。要想打开异步特性,并生成必不可少的stub代码,我们必须使用WSDL 2.0规范的自定义映射特性。

自定义使我们能够改变 wsdl2java 工具生成stub代码的方式。特别地,它允许我们修改WSDL到Java的映射,并打开某些特性。在这里,自定义的作用是打开异步调用特性。自定义是用一个绑定声明规定的,该声明是我们用一个 jaxws:bindings 标签(jaxws 前缀绑定到 http://java.sun.com/xml/ns/jaxws 命名空间)定义的。指定一个绑定声明有两种可选的方式:

  • 外部绑定声明 - jaxws:bindings 元素被定义在WSDL契约之外的一个单独的文件。生成stub代码的时候,我们需要对wsdl2java 工具指定绑定声明文件的位置。
  • 嵌入式绑定声明 - 我们也可以直接把jaxws:bindings 元素嵌入到 WSDL 契约中,把它当做WSDL的扩展。在这种情况下,jaxws:bindings 的设置仅对直接的父元素起作用。

本文只考虑第一种方法,即外部绑定声明。一个打开了异步调用开关的绑定声明文件的模版如下所示:

[java]  view plain  copy
  1. <bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
  2.           xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  
  3.           wsdlLocation="http://localhost:9000/helloWorld?wsdl"  
  4.           xmlns="http://java.sun.com/xml/ns/jaxws">  
  5.   <bindings node="wsdl:definitions">  
  6.     <enableAsyncMapping>true</enableAsyncMapping>  
  7.   </bindings>  
  8. </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的服务端点接口定义如下:

[java]  view plain  copy
  1. import java.util.concurrent.Future;  
  2. import javax.jws.WebMethod;  
  3. import javax.jws.WebParam;  
  4. import javax.jws.WebResult;  
  5. import javax.jws.WebService;  
  6. import javax.xml.bind.annotation.XmlSeeAlso;  
  7. import javax.xml.ws.AsyncHandler;  
  8. import javax.xml.ws.RequestWrapper;  
  9. import javax.xml.ws.Response;  
  10. import javax.xml.ws.ResponseWrapper;  
  11.   
  12.   
  13. @WebService(targetNamespace = "http://service.server.cxf.test.neareast.com/", name = "iHelloWorld")  
  14. @XmlSeeAlso({ObjectFactory.class})  
  15. public interface IHelloWorld {  
  16.   
  17.     @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")  
  18.     @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")  
  19.     @WebMethod(operationName = "sayHi")  
  20.     public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> sayHiAsync(  
  21.         @WebParam(name = "text", targetNamespace = "")  
  22.         java.lang.String text  
  23.     );  
  24.   
  25.     @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")  
  26.     @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")  
  27.     @WebMethod(operationName = "sayHi")  
  28.     public Future<?> sayHiAsync(  
  29.         @WebParam(name = "text", targetNamespace = "")  
  30.         java.lang.String text,  
  31.         @WebParam(name = "asyncHandler", targetNamespace = "")  
  32.         AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> asyncHandler  
  33.     );  
  34.   
  35.     @WebResult(name = "return", targetNamespace = "")  
  36.     @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")  
  37.     @WebMethod  
  38.     @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")  
  39.     public java.lang.String sayHi(  
  40.         @WebParam(name = "text", targetNamespace = "")  
  41.         java.lang.String text  
  42.     );  
  43.   
  44.     @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")  
  45.     @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")  
  46.     @WebMethod(operationName = "sayHiToUser")  
  47.     public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> sayHiToUserAsync(  
  48.         @WebParam(name = "arg0", targetNamespace = "")  
  49.         com.neareast.test.cxf.asyClient.WSDL2Java.User arg0  
  50.     );  
  51.   
  52.     @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")  
  53.     @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")  
  54.     @WebMethod(operationName = "sayHiToUser")  
  55.     public Future<?> sayHiToUserAsync(  
  56.         @WebParam(name = "arg0", targetNamespace = "")  
  57.         com.neareast.test.cxf.asyClient.WSDL2Java.User arg0,  
  58.         @WebParam(name = "asyncHandler", targetNamespace = "")  
  59.         AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> asyncHandler  
  60.     );  
  61.   
  62.     @WebResult(name = "return", targetNamespace = "")  
  63.     @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")  
  64.     @WebMethod  
  65.     @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")  
  66.     public java.lang.String sayHiToUser(  
  67.         @WebParam(name = "arg0", targetNamespace = "")  
  68.         com.neareast.test.cxf.asyClient.WSDL2Java.User arg0  
  69.     );  
  70.   
  71.     @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")  
  72.     @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")  
  73.     @WebMethod(operationName = "getUsers")  
  74.     public Response<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> getUsersAsync();  
  75.   
  76.     @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")  
  77.     @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")  
  78.     @WebMethod(operationName = "getUsers")  
  79.     public Future<?> getUsersAsync(  
  80.         @WebParam(name = "asyncHandler", targetNamespace = "")  
  81.         AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> asyncHandler  
  82.     );  
  83.   
  84.     @WebResult(name = "return", targetNamespace = "")  
  85.     @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")  
  86.     @WebMethod  
  87.     @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")  
  88.     public com.neareast.test.cxf.asyClient.WSDL2Java.IntegerUserMap getUsers();  
  89. }  

除了原来的同步方法(如sayHi方法),sayHi操作的两个异步调用方法也被同时生成了:

  • 返回值类型为Future<?>,有一个类型为javax.xml.ws.AsyncHandler的额外参数的sayHiAsync()方法 —— 该方法可用于异步调用的回调方式。
  • 返回值类型为Response<GreetMeSometimeResponse>的sayHiAsync()方法 —— 该方法可用于异步调用的轮询方式。

回调方式和轮询方式的细节将在下面的章节讨论。为体现异步调用的特点,笔者修改了教程(1)中Helloworld服务的部分实现,在sayHiToUser()方法中加入了3秒钟的休眠,并增强了代码的鲁棒性,改动如下:

[java]  view plain  copy
  1. public String sayHiToUser(User user) {  
  2.     String retVal = null;  
  3.     if(null == user){  
  4.         retVal = "Error: user object null !";  
  5.     }else{  
  6.         try{  
  7.             System.out.println("sleep for 3 seconds before return");  
  8.             Thread.sleep(3000);  
  9.         }catch(InterruptedException e){  
  10.             e.printStackTrace();  
  11.         }  
  12.           
  13.         System.out.println("sayHiToUser called by: " + user.getName());  
  14.         users.put(users.size() + 1, user);  
  15.         retVal = "Hello " + user.getName();  
  16.     }  
  17.     return retVal;  
  18. }  

实现一个轮询方式的异步调用客户端

下面的代码演示了异步发送操作调用的轮询方式的实现。客户端是通过特殊的Java方法 _OperationName_Async(本例为sayHiAsync()方法)来调用这个操作的,该方法返回一个javax.xml.ws.Response<T> 对象,其中“T”是这个操作的响应消息的类型(本例中为SayHiResponse类型)。我们可以稍后通过轮询Response<T> 对象来检查该操作的响应消息是否已经到达。 

[java]  view plain  copy
  1. package com.neareast.test.cxf.asyClient.consumer;  
  2.   
  3. import java.util.concurrent.ExecutionException;  
  4.   
  5. import javax.xml.ws.Response;  
  6.   
  7. import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld;  
  8. import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld;  
  9. import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse;  
  10.   
  11. public class BasicClientPolling {  
  12.     public static void main(String[] args) throws InterruptedException{  
  13.         HelloWorld server = new HelloWorld();  
  14.         IHelloWorld hello = server.getHelloWorldImplPort();  
  15.   
  16.         Response<SayHiResponse> sayHiResponseResp = hello.sayHiAsync(System.getProperty("user.name"));  
  17.         while (!sayHiResponseResp.isDone()) {  
  18.             Thread.sleep(100);  
  19.         }  
  20.   
  21.         try {  
  22.             SayHiResponse reply = sayHiResponseResp.get();  
  23.             System.out.println( reply.getReturn() );  
  24.         } catch (ExecutionException e) {  
  25.             e.printStackTrace();  
  26.         }  
  27.           
  28.     }  
  29. }  

sayHiAsync()方法调用了sayHi操作,将输入参数传送到远程的服务,并返回javax.xml.ws.Response<SayHiResponse> 对象的一个引用。Response 类实现了标准的 java.util.concurrency.Future<T> 接口,该类设计用来轮询一个并发线程执行的任务的产出结果。本质上来说,使用Response对象来轮询有两种基本方法:

  • Non-blocking polling(非阻塞轮询) - 尝试获得结果之前,调用非阻塞方法Response<T>.isDone()来检查响应消息是否到达,例如:
    [java]  view plain  copy
    1. <pre name="code" class="java">    User u = new User();  
    2.           
    3.     //非阻塞式轮询  
    4.     u.setName(System.getProperty("user.name"));  
    5.     Response<SayHiToUserResponse> sayHiToUserResponseResp = hello.sayHiToUserAsync(u);  
    6.     while (!sayHiToUserResponseResp.isDone()) {  
    7.         Thread.sleep(100);  
    8.     }  
    9.           
    10.     try {  
    11.         //如果没有前面isDone的检测,此处就退化为阻塞式轮询  
    12.         SayHiToUserResponse reply = sayHiToUserResponseResp.get();  
    13.         System.out.println( reply.getReturn() );  
    14.     } catch (ExecutionException e) {  
    15.         e.printStackTrace();  
    16.     }</pre><br>  
    17. <pre></pre>  
    18. <pre></pre>  
    19. <pre></pre>  
    20. <pre></pre>  
    21. <pre></pre>  
    22. <pre></pre>  
    23. <pre></pre>  
  • Blocking polling(阻塞轮询) - 立即调用Response<T>.get(),阻塞至响应到达(可以指定一个超时时长作为可选项)。例如,轮询一个响应,超时时长为60s:
    [java]  view plain  copy
    1. //阻塞式轮询  
    2. u.setName("NearEast");  
    3. sayHiToUserResponseResp = hello.sayHiToUserAsync(u);  
    4. try {  
    5.     SayHiToUserResponse reply = sayHiToUserResponseResp.get(5L,java.util.concurrent.TimeUnit.SECONDS);  
    6.     System.out.println( reply.getReturn() );  
    7. catch (ExecutionException e) {  
    8.     e.printStackTrace();  
    9. catch (TimeoutException e) {  
    10.     e.printStackTrace();  
    11. }  

实现一个回调方式的异步调用客户端

发起异步操作调用的另一个可选方法是实现javax.xml.ws.AsyncHandler接口,派生出一个回调类。回调类必须实现 handleResponse() 方法,CXF运行时调用这个类将响应的到达通知给客户端。下面的代码给出了我们需要实现的 AsyncHandler 接口的轮廓。

The javax.xml.ws.AsyncHandler Interface
<span class="code-keyword" style="color: rgb(0, 0, 145); background-color: inherit; ">package</span> javax.xml.ws;

<span class="code-keyword" style="color: rgb(0, 0, 145); background-color: inherit; ">public</span> <span class="code-keyword" style="color: rgb(0, 0, 145); background-color: inherit; ">interface</span> AsyncHandler<T>
{
  void handleResponse(Response<T> res);
}

本例使用一个测试用的回调类 SayHiToUserAsyHandler,代码如下:

[java]  view plain  copy
  1. package com.neareast.test.cxf.asyClient.consumer;  
  2.   
  3. import java.util.concurrent.ExecutionException;  
  4.   
  5. import javax.xml.ws.AsyncHandler;  
  6. import javax.xml.ws.Response;  
  7.   
  8. import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse;  
  9.   
  10. public class SayHiToUserAsyHandler implements AsyncHandler<SayHiToUserResponse> {  
  11.     SayHiToUserResponse reply = null;  
  12.   
  13.     @Override  
  14.     public void handleResponse(Response<SayHiToUserResponse> res) {  
  15.         try {  
  16.             reply = res.get();  
  17.             System.out.println( reply.getReturn() );  
  18.         } catch (ExecutionException e) {  
  19.             e.printStackTrace();  
  20.         } catch (InterruptedException e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.     }  
  24.       
  25.     public String getResponseText(){  
  26.         return reply.getReturn();  
  27.     }  
  28.   
  29. }  

上述 handleResponse() 的实现只是简单地获取响应数据,并把它存放到成员变量reply中。额外的getResponseText() 方法是为了方便地从响应中提炼出主要的输出参数。

下面的代码演示了发起异步操作调用的回调方法。客户端通过特定的Java方法 _OperationName_Async()来调用相应的操作,该方法使用一个额外的AsyncHandler<T>类型的参数,并返回一个 java.util.concurrency.Future<?> 对象。

[java]  view plain  copy
  1. package com.neareast.test.cxf.asyClient.consumer;  
  2.   
  3. import java.util.concurrent.Future;  
  4.   
  5. import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld;  
  6. import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld;  
  7. import com.neareast.test.cxf.asyClient.WSDL2Java.User;  
  8.   
  9. public class BasicCallbackClient {  
  10.     public static void main(String[] args) throws InterruptedException{  
  11.         HelloWorld server = new HelloWorld();  
  12.         IHelloWorld hello = server.getHelloWorldImplPort();  
  13.           
  14.         User u = new User();  
  15.         //非阻塞式轮询  
  16.         u.setName(System.getProperty("user.name"));  
  17.         SayHiToUserAsyHandler asyHandler = new SayHiToUserAsyHandler();  
  18.         Future<?> res = hello.sayHiToUserAsync(u, asyHandler);  
  19.         while (!res.isDone()) {  
  20.             Thread.sleep(100);  
  21.         }  
  22.           
  23.         String reply = asyHandler.getResponseText();  
  24.         System.out.println( reply );  
  25.           
  26.     }  
  27. }  

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值