Sunday 2 March 2014

CXF Asynchronous API

Client API
WSDL asynchronious binding
If you use WSDL first approach and code is generated, it will be necessary to create asynchronous binding declaration using following template:

<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
          wsdlLocation="<at:var at:name="WSDL_LOCATION" />/hello_world_async.wsdl"
          xmlns="http://java.sun.com/xml/ns/jaxws">
  <bindings node="wsdl:definitions">
    <enableAsyncMapping>true</enableAsyncMapping>
  </bindings>
</bindings>


This node attribute in binding is an XPath value that specifies which node (or nodes) from the WSDL contract are affected by this binding declaration. In this template node is set to wsdl:definitions, that means entire WSDL contract to be affected.
Than you should add file containing this binding as parameter to cxf-codegen-plugin:

<plugin>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-codegen-plugin</artifactId>
   <version>${project.version}</version>
   <executions>
       <execution>
          <id>generate-sources</id>
          <phase>generate-sources</phase>
           <configuration>
               <wsdlOptions>
                  <wsdlOption>
                      <wsdl>${basedir}/wsdl/hello_world_async.wsdl</wsdl>
                       <frontEnd>jaxws21</frontEnd>
                        <extraargs>
                           <extraarg>-b</extraarg>
                           <extraarg>${basedir}/wsdl/async_binding.xml</extraarg>
                        </extraargs>
                    </wsdlOption>
                 </wsdlOptions>
             </configuration>
             <goals>
                 <goal>wsdl2java</goal>
             </goals>
      </execution>
   </executions>
</plugin>


To each synchronous method Code generator will add two new asynchronous ones:

public Future<?> methodNameAsync(Type requestType, AsyncHandler<Type> asyncHandler);

public Response<Type> methodNameAsync(Type requestType);

Now you can invoke service method in non-blocking manner on client side. Both mechanisms java Future and callback are available. CXF illustrates this approach in sample and documents in wiki.

Dispatch and Dynamic Clients
If you use Dispatch client API, the method invokeAsync is available for non-blocking calls:
Future<?> invokeAsync(T msg, AsyncHandler<T> handler)
Method for non-blocking call is also available in CXF Dynamic Client:
void invoke(ClientCallback callback, BindingOperationInfo oi, Object... params)

Service API
Asynchronous API on service side means that service implementation can start a new thread for processing request, return from the operation method and set the response in new thread later:

public Future<?> myMethodAsync(final String me,
                                           final AsyncHandler<String> asyncHandler) {     
        final ServerAsyncResponse<MyType> r
            = new ServerAsyncResponse<MyType>();
        new Thread() {
            public void run() {
                MyTyperesp = new MyType();
                resp.setResponseType("[async] How are you " + me);
                r.set(resp);
                asyncHandler.handleResponse(r);
            }
        } .start();
        return r;
    }


CXF provides @UseAsyncMethod annotaion to achieve service side asynchronicity. How it works? Basically very simple:

@UseAsyncMethod
public String myMethod(final String me){
...
}

public Future<?> myMethodAsync(final String me,
                               final AsyncHandler<String> asyncHandler) {
...
}


You should do two things:
  • introduce new method with original name and "Async" suffix. This method will have Future<Type> as return type and AsyncHandler<Type> as additional argument.
  •  add @UseAsyncMethod annotation to original method.
Important here is that second asynchronous method will be invoked ONLY if transport supports asynchronous request processing, otherwise CXF will call first synchronous method. Asynchronous request processing will be supported in following cases:
  1. JMS transport
  2. Jetty Continuations (supported starting from Jetty 6)
  3. Servlet 3.0 API
  4. CXF Decoupled responses
Notice: this approach works not only for generated code, but for Java first and even for Provider<T> based services. In the last case you need to add the second invokeAsync method:

@WebServiceProvider
@ServiceMode(value = Service.Mode.PAYLOAD)
public class ServiceHandler implements Provider<StreamSource> {
   
    @Override
    @UseAsyncMethod
    public StreamSource invoke(StreamSource request) {
     ...
    }
   
    public Future<?> invokeAsync(final StreamSource s, final AsyncHandler<Source> asyncHandler) {
     ...    }
}


Metro provides a bit different approach to support service side asynchronous API using AsyncProvider for Provider based services. Benefit of the CXF @UseAsyncMethod annottaion is that it works for different use cases: generated stubs, java first stubs, Provider based services.

This sample and system test illustrate using of @UseAsyncMethod annotation in different cases.
 

No comments:

Post a Comment