Part 1 and Part 2 of this series described the JAX-WS client APIs and how to use of the JAX-WS programming model to create dispatch and dynamic proxy clients. In this article, we'll continue our discussion of the client APIs and show you how to create an asynchronous Web service client.
This article does the following:
- Introduces the asynchronous programming model
- Provides examples of the polling and callback models
- Describes the asynchronous message exchange pattern (MEP)
JAX-WS 2.0 asynchronous programming model
In Part 2, you learned about the JAX-WS dynamic proxy client. In this article, you'll learn about the asynchronous programming model. The asynchronous programming model is one of the major changes between JAX-WS and JAX-RPC, which provided only synchronous APIs.
Our last article used a simplified banking application. Following is the method
signature for the createAccount method:
int createAccount(String customerName, double initialBalance); |
Invoking this method synchronously assumes that the request can probably be completed in a few seconds. However, modern applications, especially in service-oriented architectures, may take considerably longer. Typical requirements to create a new bank account may include credit checks, address verification, checks with various government agencies and even a manual workflow.
The JAX-WS programming model offers two models for invoking operations asynchronously â polling and callback. Both methods allow the client to continue processing while waiting for a response.
Asynchronous service endpoint interface (SEI)
Listing 1 shows the sample WSDL file from Part 2 of this series, which we'll also use for this article. Notice that we havenât changed anything in the WSDL to make it asynchronous.
Listing 1. BankingService.wsdl WSDL definition
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="BankingService"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.example.com/services/Banking"
xmlns:types="http://www.example.com/schemas/Banking"
targetNamespace="http://www.example.com/services/Banking">
<types>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com/schemas/Banking">
<!--
definition of createAccount operation request and response beans
-->
<element name="createAccount">
<complexType>
<sequence>
<element name="owner" type="string" />
<element name="initialBalance" type="double" />
</sequence>
</complexType>
</element>
<element name="createAccountResponse">
<complexType>
<sequence>
<element name="accountNumber" type="long" />
</sequence>
</complexType>
</element>
<!--
definition of withdraw operation request, response and fault beans
-->
<element name="withdraw">
<complexType>
<sequence>
<element name="accountNumber" type="long" />
<element name="amount" type="double" />
</sequence>
</complexType>
</element>
<element name="withdrawResponse">
<complexType>
<sequence>
<element name="amount" type="double" />
</sequence>
</complexType>
</element>
<element name="InsufficientFunds">
<complexType>
<sequence>
<element name="errorMessage" type="string" />
<element name="errorCode" type="int" />
</sequence>
</complexType>
</element>
<!--
definition of getAccountInfo operation request and response beans
-->
<element name="getAccountInfo">
<complexType>
<sequence>
<element name="accountNumber" type="long" />
</sequence>
</complexType>
</element>
<element name="getAccountInfoResponse">
<complexType>
<sequence>
<element name="balance" type="double" />
<element name="owner" type="string" />
</sequence>
</complexType>
</element>
</schema>
</types>
<message name="createAccountRequest">
<part name="request" element="types:createAccount"/>
</message>
<message name="createAccountResponse">
<part name="response" element="types:createAccountResponse"/>
</message>
<message name="withdrawRequest">
<part name="request" element="types:withdraw "/>
</message>
<message name="withdrawResponse">
<part name="response" element="types:withdrawResponse"/>
</message>
<message name="InsufficientFunds">
<part name="error" element="types:InsufficientFunds"/>
</message>
<message name="getAccountInfoRequest">
<part name="request" element="types:getAccountInfo"/>
</message>
<message name="getAccountInfoResponse">
<part name="response" element="types:getAccountInfoResponse"/>
</message>
<portType name="BankingSEI">
<operation name="createAccount">
<input message="tns:createAccountRequest"/>
<output message="tns:createAccountResponse"/>
</operation>
<operation name="withdraw">
<input message="tns:withdrawRequest"/>
<output message="tns:withdrawResponse"/>
<fault message="tns:InsufficientFunds"/>
</operation>
<operation name="getAccountInfo">
<input message="tns:getAccountInfoRequest"/>
<output message="tns:getAccountInfoResponse"/>
</operation>
</portType>
<binding name="BankingSoap11Binding" type="tns:BankingSEI">
<soap:binding
style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="createAccount">
<soap:operation soapAction="createAccount"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name=" withdraw ">
<soap:operation soapAction="withdraw"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault>
<soap:fault name="InsufficientFunds" use="literal"/>
</fault>
</operation>
<operation name="getAccountInfo">
<soap:operation soapAction="getAccountInfo"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="BankingService">
<port binding="tns: BankingSoap11Binding" name="AccountsPort">
<soap:address
location="http://localhost:8080/banking/services
/BankingService"/>
</port>
</service>
</definitions>
|
To generate portable artifacts with the WSDL in Listing 1, you can use tools like
the WebSphere Application Server Toolkit or the wsimport command line tool, located at WAS_HOME/bin/wsimport. In
this article, we'll use wsimport. To use wsimport to generate asynchronous
artifacts, you need a separate binding customization file, as shown in Listing 2.
Listing 2. binding.xml binding customization file
<bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
wsdlLocation="BankingService.wsdl"
xmlns="http://java.sun.com/xml/ns/jaxws">
<bindings node="wsdl:definitions">
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
</bindings>
|
Listing 2 shows a simple binding customization that will generate asynchronous
versions of all methods found in the WSDL. Note that the
wsdlLocation element must
match the WSDL file name. Invoke the wsimport tool by
entering the following:
%WAS_HOME%\bin\wsimport.bat âb binding.xml
BankingService.xml
The generated SEI will have the following operations defined for createAccount:
Listing 3. SEI for createAccount
// Synchronous method
int createAccount(String customerName, double initialBalance);
// Polling method
javax.xml.ws.Response<CreateAccountResponse> createAccount(String customerName,
double initialBalance);
// Callback method
javax.concurrent.Future<?> createAccount(String customerName, double initialBalance,
AsyncHandler<CreateAccountResponse> handler);
|
Asynchronous polling clients are useful for implementing Web services based work flows. In a polling client, once the initial request is made the client is free to pursue other work until the request completes. This enables the client to execute other tasks that don't depend on the outcome of the request. Once the request completes, the response is made available to the client.
Table 1 describes the behavior of the javax.xml.ws.Response object.
Table 1. javax.xml.ws.Response object behavior
cancel()
| Cancel the response processing. This does not stop the server from processing the request. |
get()
| Retrieve the response, if it is available. If the response is not available, the method blocks. |
get(
long timeout, TimeUnit unit)
| Retrieve the response, but only block for a specified period of time. |
isDone()
| Check whether the response is available. |
isCancelled()
| Check whether the response processing was cancelled. |
getContext
()
| Returns the BindingProvider responseContext associated with the request. |
Listing 4 shows an example of a client making an asynchronous polling request.
Listing 4. Polling example
// Polling method
Response<CreateAccountResponse> response = createAccount
("Joe Customer", 10.00);
// wait for the response
while (!response.isDone()){
// do some work that does not depend on the new account being available
â¦
}
// retrieve the account number
try {
int accountNumber = response.get().getAccountNumber();
} catch (CancellationException ce) {
// processing was cancelled via response.cancel()
} catch (ExecutionException ee} {
// there was an error processing the request
// getCause() returns the actual exception
Systemout.println(ee.getCause());
}
|
One advantage of the polling method is that the response is not actually
processed until the response.get() method is called, so the client has full control
over when the response is processed. In this way, it is very similar to its
synchronous counterpart.
Asynchronous callback clients are useful for implementing batch processing and notification consumer applications. In a callback client, once the request is made, the client is free to pursue other work; however, the response is not made available to the client. The actual response is processed by the specified AsyncHandler implementation. Once the response is made available to the callback handler, the code flow is much the same as with polling client.
Table 2 describes how javax.concurrent.Future behaves in an asynchronous callback client.
Table 2. javax.concurrent.Future behavior in an asynchronous callback client
cancel()
| Cancel the response processing. This does not stop the server from processing the request. |
get()
| Clients should not call this method because its behavior is indeterminate. |
get(
long timeout, TimeUnit unit)
| Clients should not call this method as its behavior is indeterminate |
isDone()
| Check whether the response is available. |
isCancelled()
| Check whether the response processing was cancelled. |
Listing 5 shows the client invoking the asynchronous callback method. Note that the while loop is completely optional, as the actual response (including any failures in delivering or processing the request) will be delivered to the callback handler.
Listing 5. Callback invocation
// Callback method
CreateAcountHandler handler = new CreateAcountHandler();
Future>?< monitor = createAccount("Joe Customer", 10.00, handler);
// wait for the response
while (!monitor.isDone()){
// do some work that does not depend on the new account being available
â¦
}
|
Listing 6 shows the implementation of the Async handler.
Listing 6. AsyncHandler implementation
public class CreateAcountHandler implements AsyncHandler<CreateAccountResponse> {
public void handleResponse(Response<CreateAccountResponse> response) {
// retrieve the account number
try {
int accountNumber = response.get().getAccountNumber();
} catch (CancellationException ce) {
// processing was cancelled via response.cancel()
} catch (ExecutionException ee} {
// there was an error processing the request
// getCause() returns the actual exception
Systemout.println(ee.getCause());
}
}
}
|
Callback calls process the response on a different thread from the one the client
is running on. This allows the client to gain some control over performance if
there are many concurrent asynchronous requests.
You can get this control by providing an Executor using the BankingService.setExecutor()
method. Executors provide both a thread pool and a means for the JAX-WS run-time to
queue asynchronous responses for processing. Listing 7 shows an example of
how to use an Executors factory to create a thread pool of 10 threads.
Listing 7. Executor example
BankingService svc = new BankingService();
svc.setExecutor(java.util.concurrent.Executors.newFixedThreadPool(10));
|
WebSphere provides its own default Executor, so providing your own Executor is optional.
So far in our discussion, we've only talked about the asynchronous programming
model, in which the actual requests are synchronous and are affected by socket read and
write timeouts. To make these requests asynchronous at the HTTP connection level,
you have to set additional properties in the request context. To enable the
asynchronous message exchange, set
com.ibm.websphere.webservices.use.async.mep to
true in the request context.
Listing 8. Enable asynchronous message exchange
// Create a Dynamic Proxy client
BankingService service = new BankingService();
BankingSEI port = service.getAccountsPort();
// Use Proxy Instance as BindingProvider
BindingProvider bp = (BindingProvider) port;
Map<String, Object> rc = bp.getRequestContext();
rc.put("com.ibm.websphere.webservices.use.async.mep", Boolean.TRUE);
// invoke the operation asynchronously
|
When asynchronous message exchange is enabled, WS-Addressing support is enabled
and a wsa:ReplyTo header is added to the SOAP request. The HTTP request is
immediately acknowledged and the original connection is closed. The client then
starts a local HTTP server and listens for the response from the endpoint.
Note that this property is specific to IBM's implementation of JAX-WS and won't work on other vendorâs implementations. It is, however, compatible with any endpoint that supports WS-Addressing,k such as other WebSphere Application Server or Microsoft .NET endpoints.
JAX-WS introduces an asynchronous programming model via polling and callback APIs. Asynchronous Web service calls are non-blocking, which means that an application can invoke a Web service and can continue running the business logic without waiting for a response. The introduction of such a powerful programming model, which was lacking in JAX-RPC, is a good enough reason for customers to migrate from JAX-RPC to new JAX-WS.
Learn
-
JAX-WS
client APIs in the Web Services Feature Pack for WebSphere Application Server V6.1, Part
1: Creating a Dispatch client
(developerWorks 2007): Part 1 of this series guides you through creating a
Dispatch client using the various JAX-WS client APIs.
-
JAX-WS
client APIs in the Web Services Feature Pack for WebSphere Application Server V6.1, Part
2: Creating a proxy client
(developerWorks 2007): Part 2 of this series guides you through creating a proxy client.
-
JSR 224: Java API for XML-Based Web Services (JAX-WS) 2.0:
The specification.
-
JSR 181: Web Services Metadata for the Java Platform:
The specification.
-
Web Services Description Language (WSDL) 1.1:
The WSDL specification.
-
SOAP 1.2 Primer: An introduction
to the SOAP 1.2 specification.
-
The WS-I's profiles:
Particularly relevant is the basic profile.
-
developerWorks WebSphere Web services zone:
Technical resources, such as articles, tutorials, and downloads, on WebSphere Web
services solutions.
-
developerWorks WebSphere Application Server zone:
Technical resources, such as articles, tutorials, and downloads, on WebSphere
Application Server.
-
developerWorks WebSphere SOA zone:
Technical resources, such as articles, tutorials, and downloads, on WebSphere SOA
solutions.
Get products and technologies
-
Web Services Feature Pack on WebSphere Application Server V6.1 download:
Get the Feature Pack.
Discuss
-
developerWorks Forum: Web Services Feature Pack on WebSphere Application Server V6.1:
Discuss the Feature Pack with other developers.

Dan Sedov is a Staff Software Engineer performing function testing in IBM Software Group for the Web services component of WebSphere Application Server. For the past two years, Dan has been a member of the WebSphere product team that developed and tested the first Web Services Feature Pack. He has worked on creating and automating JAX-WS Web services engine tests. You can reach Dan at sedov@us.ibm.com.

Nikhil Thaker is a Staff Software Engineer with IBM Software Group, and a member of the WebSphere product team that developed Web services feature pack. He has more than nine years of experience in Enterprise Application Integration, and has focused on Web services for the past two years. He has worked with various IBM customers as an IT Specialist in Enterprise Application Integration in IBM Global Services. His industry exposure includes Automotive, Health Care, telecommunication and utilities. You can reach Nikhil at nikhil.v.thaker@us.ibm.com.




