Ce contenu n'est pas disponible dans la langue sélectionnée.
Chapter 40. Developing Asynchronous Applications
Abstract
JAX-WS provides an easy mechanism for accessing services asynchronously. The SEI can specify additional methods that can be used to access a service asynchronously. The Apache CXF code generators generate the extra methods for you. You simply add the business logic.
40.1. Types of Asynchronous Invocation
In addition to the usual synchronous mode of invocation, Apache CXF supports two forms of asynchronous invocation:
-
Polling approach — To invoke the remote operation using the polling approach, you call a method that has no output parameters, but returns a
javax.xml.ws.Response
object. TheResponse
object (which inherits from the javax.util.concurrency.Future interface) can be polled to check whether or not a response message has arrived. -
Callback approach — To invoke the remote operation using the callback approach, you call a method that takes a reference to a callback object (of
javax.xml.ws.AsyncHandler
type) as one of its parameters. When the response message arrives at the client, the runtime calls back on theAsyncHandler
object, and gives it the contents of the response message.
40.2. WSDL for Asynchronous Examples
Example 40.1, “WSDL Contract for Asynchronous Example” shows the WSDL contract that is used for the asynchronous examples. The contract defines a single interface, GreeterAsync, which contains a single operation, greetMeSometime.
Example 40.1. WSDL Contract for Asynchronous Example
<?xml version="1.0" encoding="UTF-8"?><wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://apache.org/hello_world_async_soap_http" xmlns:x1="http://apache.org/hello_world_async_soap_http/types" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://apache.org/hello_world_async_soap_http" name="HelloWorld"> <wsdl:types> <schema targetNamespace="http://apache.org/hello_world_async_soap_http/types" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:x1="http://apache.org/hello_world_async_soap_http/types" elementFormDefault="qualified"> <element name="greetMeSometime"> <complexType> <sequence> <element name="requestType" type="xsd:string"/> </sequence> </complexType> </element> <element name="greetMeSometimeResponse"> <complexType> <sequence> <element name="responseType" type="xsd:string"/> </sequence> </complexType> </element> </schema> </wsdl:types> <wsdl:message name="greetMeSometimeRequest"> <wsdl:part name="in" element="x1:greetMeSometime"/> </wsdl:message> <wsdl:message name="greetMeSometimeResponse"> <wsdl:part name="out" element="x1:greetMeSometimeResponse"/> </wsdl:message> <wsdl:portType name="GreeterAsync"> <wsdl:operation name="greetMeSometime"> <wsdl:input name="greetMeSometimeRequest" message="tns:greetMeSometimeRequest"/> <wsdl:output name="greetMeSometimeResponse" message="tns:greetMeSometimeResponse"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="GreeterAsync_SOAPBinding" type="tns:GreeterAsync"> ... </wsdl:binding> <wsdl:service name="SOAPService"> <wsdl:port name="SoapPort" binding="tns:GreeterAsync_SOAPBinding"> <soap:address location="http://localhost:9000/SoapContext/SoapPort"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
40.3. Generating the Stub Code
Overview
The asynchronous style of invocation requires extra stub code for the dedicated asynchronous methods defined on the SEI. This special stub code is not generated by default. To switch on the asynchronous feature and generate the requisite stub code, you must use the mapping customization feature from the WSDL 2.0 specification.
Customization enables you to modify the way the Maven code generation plug-in generates stub code. In particular, it enables you to modify the WSDL-to-Java mapping and to switch on certain features. Here, customization is used to switch on the asynchronous invocation feature. Customizations are specified using a binding declaration, which you define using a jaxws:bindings
tag (where the jaxws
prefix is tied to the http://java.sun.com/xml/ns/jaxws
namespace). There are two ways of specifying a binding declaration:
- External Binding Declaration
-
When using an external binding declaration the
jaxws:bindings
element is defined in a file separate from the WSDL contract. You specify the location of the binding declaration file to code generator when you generate the stub code. - Embedded Binding Declaration
-
When using an embedded binding declaration you embed the
jaxws:bindings
element directly in a WSDL contract, treating it as a WSDL extension. In this case, the settings injaxws:bindings
apply only to the immediate parent element.
Using an external binding declaration
The template for a binding declaration file that switches on asynchronous invocations is shown in Example 40.2, “Template for an Asynchronous Binding Declaration”.
Example 40.2. Template for an Asynchronous Binding Declaration
<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" wsdlLocation="AffectedWSDL" xmlns="http://java.sun.com/xml/ns/jaxws"> <bindings node="AffectedNode"> <enableAsyncMapping>true</enableAsyncMapping> </bindings> </bindings>
Where AffectedWSDL specifies the URL of the WSDL contract that is affected by this binding declaration. The AffectedNode is an XPath value that specifies which node (or nodes) from the WSDL contract are affected by this binding declaration. You can set AffectedNode to wsdl:definitions
, if you want the entire WSDL contract to be affected. The jaxws:enableAsyncMapping
element is set to true
to enable the asynchronous invocation feature.
For example, if you want to generate asynchronous methods only for the GreeterAsync interface, you can specify <bindings node="wsdl:definitions/wsdl:portType[@name='GreeterAsync']"> in the preceding binding declaration.
Assuming that the binding declaration is stored in a file, async_binding.xml
, you would set up your POM as shown in Example 40.3, “Consumer Code Generation”.
Example 40.3. Consumer Code Generation
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>outputDir</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>hello_world.wsdl</wsdl>
<extraargs>
<extraarg>-client</extraarg>
<extraarg>-b async_binding.xml</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
The -b
option tells the code generator where to locate the external binding file.
For more information on the code generator see Section 44.2, “cxf-codegen-plugin”.
Using an embedded binding declaration
You can also embed the binding customization directly into the WSDL document defining the service by placing the jaxws:bindings
element and its associated jaxws:enableAsynchMapping
child directly into the WSDL. You also must add a namespace declaration for the jaxws
prefix.
Example 40.4, “WSDL with Embedded Binding Declaration for Asynchronous Mapping” shows a WSDL file with an embedded binding declaration that activates the asynchronous mapping for an operation.
Example 40.4. WSDL with Embedded Binding Declaration for Asynchronous Mapping
<wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/" ... xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" ...> ... <wsdl:portType name="GreeterAsync"> <wsdl:operation name="greetMeSometime"> <jaxws:bindings> <jaxws:enableAsyncMapping>true</jaxws:enableAsyncMapping> </jaxws:bindings> <wsdl:input name="greetMeSometimeRequest" message="tns:greetMeSometimeRequest"/> <wsdl:output name="greetMeSometimeResponse" message="tns:greetMeSometimeResponse"/> </wsdl:operation> </wsdl:portType> ... </wsdl:definitions>
When embedding the binding declaration into the WSDL document you can control the scope affected by the declaration by changing where you place the declaration. When the declaration is placed as a child of the wsdl:definitions
element the code generator creates asynchronous methods for all of the operations defined in the WSDL document. If it is placed as a child of a wsdl:portType
element the code generator creates asynchronous methods for all of the operations defined in the interface. If it is placed as a child of a wsdl:operation
element the code generator creates asynchronous methods for only that operation.
It is not necessary to pass any special options to the code generator when using embedded declarations. The code generator will recognize them and act accordingly.
Generated interface
After generating the stub code in this way, the GreeterAsync SEI (in the file GreeterAsync.java
) is defined as shown in Example 40.5, “Service Endpoint Interface with Methods for Asynchronous Invocations”.
Example 40.5. Service Endpoint Interface with Methods for Asynchronous Invocations
package org.apache.hello_world_async_soap_http; import org.apache.hello_world_async_soap_http.types.GreetMeSometimeResponse; ... public interface GreeterAsync { public Future<?> greetMeSometimeAsync( java.lang.String requestType, AsyncHandler<GreetMeSometimeResponse> asyncHandler ); public Response<GreetMeSometimeResponse> greetMeSometimeAsync( java.lang.String requestType ); public java.lang.String greetMeSometime( java.lang.String requestType ); }
In addition to the usual synchronous method, greetMeSometime()
, two asynchronous methods are also generated for the greetMeSometime operation:
40.4. Implementing an Asynchronous Client with the Polling Approach
Overview
The polling approach is the more straightforward of the two approaches to developing an asynchronous application. The client invokes the asynchronous method called OperationNameAsync()
and is returned a Response<T>
object that it polls for a response. What the client does while it is waiting for a response is depends on the requirements of the application. There are two basic patterns for handling the polling:
-
Non-blocking polling— You periodically check to see if the result is ready by calling the non-blocking
Response<T>.isDone()
method. If the result is ready, the client processes it. If it not, the client continues doing other things. -
Blocking polling— You call
Response<T>.get()
right away, and block until the response arrives (optionally specifying a timeout).
Using the non-blocking pattern
Example 40.6, “Non-Blocking Polling Approach for an Asynchronous Operation Call” illustrates using non-blocking polling to make an asynchronous invocation on the greetMeSometime operation defined in Example 40.1, “WSDL Contract for Asynchronous Example”. The client invokes the asynchronous operation and periodically checks to see if the result is returned.
Example 40.6. Non-Blocking Polling Approach for an Asynchronous Operation Call
package demo.hw.client; import java.io.File; import java.util.concurrent.Future; import javax.xml.namespace.QName; import javax.xml.ws.Response; import org.apache.hello_world_async_soap_http.*; public final class Client { private static final QName SERVICE_NAME = new QName("http://apache.org/hello_world_async_soap_http", "SOAPService"); private Client() {} public static void main(String args[]) throws Exception { // set up the proxy for the client Response<GreetMeSometimeResponse> greetMeSomeTimeResp = port.greetMeSometimeAsync(System.getProperty("user.name")); while (!greetMeSomeTimeResp.isDone()) { // client does some work } GreetMeSometimeResponse reply = greetMeSomeTimeResp.get(); // process the response System.exit(0); } }
The code in Example 40.6, “Non-Blocking Polling Approach for an Asynchronous Operation Call” does the following:
Invokes the greetMeSometimeAsync()
on the proxy.
The method call returns the Response<GreetMeSometimeResponse>
object to the client immediately. The Apache CXF runtime handles the details of receiving the reply from the remote endpoint and populating the Response<GreetMeSometimeResponse>
object.
The runtime transmits the request to the remote endpoint’s greetMeSometime()
method and handles the details of the asynchronous nature of the call transparently. The endpoint, and therefore the service implementation, never worries about the details of how the client intends to wait for a response.
Checks to see if a response has arrived by checking the isDone()
of the returned Response
object.
If the response has not arrived, the client continues working before checking again.
When the response arrives, the client retrieves it from the Response
object using the get()
method.
Using the blocking pattern
When using the block polling pattern, the Response
object’s isDone()
is never called. Instead, the Response
object’s get()
method is called immediately after invoking the remote operation. The get()
blocks until the response is available.
You can also pass a timeout limit to the get()
method.
Example 40.7, “Blocking Polling Approach for an Asynchronous Operation Call” shows a client that uses blocking polling.
Example 40.7. Blocking Polling Approach for an Asynchronous Operation Call
package demo.hw.client; import java.io.File; import java.util.concurrent.Future; import javax.xml.namespace.QName; import javax.xml.ws.Response; import org.apache.hello_world_async_soap_http.*; public final class Client { private static final QName SERVICE_NAME = new QName("http://apache.org/hello_world_async_soap_http", "SOAPService"); private Client() {} public static void main(String args[]) throws Exception { // set up the proxy for the client Response<GreetMeSometimeResponse> greetMeSomeTimeResp = port.greetMeSometimeAsync(System.getProperty("user.name")); GreetMeSometimeResponse reply = greetMeSomeTimeResp.get(); // process the response System.exit(0); } }
40.5. Implementing an Asynchronous Client with the Callback Approach
Overview
An alternative approach to making an asynchronous operation invocation is to implement a callback class. You then call the asynchronous remote method that takes the callback object as a parameter. The runtime returns the response to the callback object.
To implement an application that uses callbacks, do the following:
Create a callback class that implements the AsyncHandler interface.
NoteYour callback object can perform any amount of response processing required by your application.
-
Make remote invocations using the
operationNameAsync()
that takes the callback object as a parameter and returns aFuture<?>
object. If your client requires access to the response data, you can poll the returned
Future<?>
object’sisDone()
method to see if the remote endpoint has sent the response.If the callback object does all of the response processing, it is not necessary to check if the response has arrived.
Implementing the callback
The callback class must implement the javax.xml.ws.AsyncHandler interface. The interface defines a single method: handleResponse
Response<T>
res
The Apache CXF runtime calls the handleResponse()
method to notify the client that the response has arrived. Example 40.8, “The javax.xml.ws.AsyncHandler Interface” shows an outline of the AsyncHandler interface that you must implement.
Example 40.8. The javax.xml.ws.AsyncHandler Interface
public interface javax.xml.ws.AsyncHandler { void handleResponse(Response<T> res) }
Example 40.9, “Callback Implementation Class” shows a callback class for the greetMeSometime operation defined in Example 40.1, “WSDL Contract for Asynchronous Example”.
Example 40.9. Callback Implementation Class
package demo.hw.client; import javax.xml.ws.AsyncHandler; import javax.xml.ws.Response; import org.apache.hello_world_async_soap_http.types.*; public class GreeterAsyncHandler implements AsyncHandler<GreetMeSometimeResponse> { private GreetMeSometimeResponse reply; public void handleResponse(Response<GreetMeSometimeResponse> response) { try { reply = response.get(); } catch (Exception ex) { ex.printStackTrace(); } } public String getResponse() { return reply.getResponseType(); } }
The callback implementation shown in Example 40.9, “Callback Implementation Class” does the following:
Defines a member variable, response
, that holds the response returned from the remote endpoint.
Implements handleResponse()
.
This implementation simply extracts the response and assigns it to the member variable reply
.
Implements an added method called getResponse()
.
This method is a convenience method that extracts the data from reply
and returns it.
Implementing the consumer
Example 40.10, “Callback Approach for an Asynchronous Operation Call” illustrates a client that uses the callback approach to make an asynchronous call to the GreetMeSometime operation defined in Example 40.1, “WSDL Contract for Asynchronous Example”.
Example 40.10. Callback Approach for an Asynchronous Operation Call
package demo.hw.client; import java.io.File; import java.util.concurrent.Future; import javax.xml.namespace.QName; import javax.xml.ws.Response; import org.apache.hello_world_async_soap_http.*; public final class Client { ... public static void main(String args[]) throws Exception { ... // Callback approach GreeterAsyncHandler callback = new GreeterAsyncHandler(); Future<?> response = port.greetMeSometimeAsync(System.getProperty("user.name"), callback); while (!response.isDone()) { // Do some work } resp = callback.getResponse(); ... System.exit(0); } }
The code in Example 40.10, “Callback Approach for an Asynchronous Operation Call” does the following:
Instantiates a callback object.
Invokes the greetMeSometimeAsync()
that takes the callback object on the proxy.
The method call returns the Future<?>
object to the client immediately. The Apache CXF runtime handles the details of receiving the reply from the remote endpoint, invoking the callback object’s handleResponse()
method, and populating the Response<GreetMeSometimeResponse>
object.
The runtime transmits the request to the remote endpoint’s greetMeSometime()
method and handles the details of the asynchronous nature of the call without the remote endpoint’s knowledge. The endpoint, and therefore the service implementation, does not need to worry about the details of how the client intends to wait for a response.
Uses the returned Future<?>
object’s isDone()
method to check if the response has arrived from the remote endpoint.
Invokes the callback object’s getResponse()
method to get the response data.
40.6. Catching Exceptions Returned from a Remote Service
Overview
Consumers making asynchronous requests will not receive the same exceptions returned when they make synchronous requests. Any exceptions returned to the consumer asynchronously are wrapped in an ExecutionException exception. The actual exception thrown by the service is stored in the ExecutionException exception’s cause
field.
Catching the exception
Exceptions generated by a remote service are thrown locally by the method that passes the response to the consumer’s business logic. When the consumer makes a synchronous request, the method making the remote invocation throws the exception. When the consumer makes an asynchronous request, the Response<T> object’s get()
method throws the exception. The consumer will not discover that an error was encountered in processing the request until it attempts to retrieve the response message.
Unlike the methods generated by the JAX-WS framework, the Response<T> object’s get()
method throws neither user modeled exceptions nor generic JAX-WS exceptions. Instead, it throws a java.util.concurrent.ExecutionException exception.
Getting the exception details
The framework stores the exception returned from the remote service in the ExecutionException exception’s cause
field. The details about the remote exception are extracted by getting the value of the cause
field and examining the stored exception. The stored exception can be any user defined exception or one of the generic JAX-WS exceptions.
Example
Example 40.11, “Catching an Exception using the Polling Approach” shows an example of catching an exception using the polling approach.
Example 40.11. Catching an Exception using the Polling Approach
package demo.hw.client; import java.io.File; import java.util.concurrent.Future; import javax.xml.namespace.QName; import javax.xml.ws.Response; import org.apache.hello_world_async_soap_http.*; public final class Client { private static final QName SERVICE_NAME = new QName("http://apache.org/hello_world_async_soap_http", "SOAPService"); private Client() {} public static void main(String args[]) throws Exception { ... // port is a previously established proxy object. Response<GreetMeSometimeResponse> resp = port.greetMeSometimeAsync(System.getProperty("user.name")); while (!resp.isDone()) { // client does some work } try { GreetMeSometimeResponse reply = greetMeSomeTimeResp.get(); // process the response } catch (ExecutionException ee) { Throwable cause = ee.getCause(); System.out.println("Exception "+cause.getClass().getName()+" thrown by the remote service."); } } }
The code in Example 40.11, “Catching an Exception using the Polling Approach” does the following:
Wraps the call to the Response<T> object’s get()
method in a try/catch block.
Catches a ExecutionException exception.
Extracts the cause
field from the exception.
If the consumer was using the callback approach the code used to catch the exception would be placed in the callback object where the service’s response is extracted.