JAX-WS client APIs in the Web Services Feature Pack for WebSphere Application Server V6.1, Part 3: Using the JAX-WS asynchronous programming model

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.

Share:

Dan Sedov (sedov@us.ibm.com), Staff Software Engineer, IBM

Dan Sedov photoDan 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 (nikhil.v.thaker@us.ibm.com), Staff Software Engineer, IBM

Nikhil Thaker photoNikhil 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.



30 April 2008

Also available in Chinese Russian

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:

  1. Introduces the asynchronous programming model
  2. Provides examples of the polling and callback models
  3. 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

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, SOA and web services
ArticleID=304597
ArticleTitle=JAX-WS client APIs in the Web Services Feature Pack for WebSphere Application Server V6.1, Part 3: Using the JAX-WS asynchronous programming model
publish-date=04302008