 | Level: Introductory Dan Sedov (sedov@us.ibm.com), Staff Software Engineer, IBM Nikhil Thaker (nikhil.v.thaker@us.ibm.com), Staff Software Engineer, IBM
30 Apr 2008 In the final part of this series on JAX-WS 2.0 in the WebSphere Application Server V6.1 Feature Pack
for Web Services, you'll learn how to create an
asynchronous Web client, and learn how to use the polling and callback models.
Introduction
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);
|
Polling example
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.
Callback
example
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());
}
}
}
|
Threading
considerations
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.
Asynchronous MEP
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.
Summary
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.
Resources 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
Discuss
About the authors  | 
|  |
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. |
Rate this page
|  |