Level: Intermediate Eran Chinthaka (chinthaka@apache.org), Member, Apache Software Foundation
11 Oct 2007 The implementation and invocation of asynchronous Web services is important
to application development. UIs, which expose functionalities of Web services, have
become increasingly interactive. As a result, asynchronous invocations and
implementations have become more useful and effective, ultimately helping provide a
better overall user experience. This article gives you an overview of different
patterns for asynchronous scenarios in Web services and provides insight into how to implement them with Apache Axis2.
Introduction
When first introduced as a way to do application development, Web services
implementations supported only synchronous invocations for request-response interactions
(synchronous in this article implies processing both request and response in the
same thread of execution). But when more and more applications adopted Web services to
expose functionalities and client applications were designed to interact with Web
services, mere synchronous invocations were seen as bottlenecks. This is because some
Web services implementations take a considerable amount of time to respond to requests due to various reasons inside the service implementation. For example, if a Web service requires human intervention or batch processing at some point, the Web service might take days to reach an outcome. These delays also caused some of the transport mechanisms to time out.
 | |
With the evolution of Web services development, eventually some SOAP stacks tried to
emulate asynchronous invocations on top of synchronous capabilities. Even though those
efforts gave a sense of asynchronous invocations, they couldn't provide full and real
functionality. Apache Axis2, the open source Web services engine from Apache Software
Foundation, provides first-class support for asynchronous Web services invocations and
implementation. Axis2 has two mechanisms to provide asynchronicity in the client side
and one mechanism in the server side. Let's look at how these can be used for
asynchronous communications in more detail.
Client-side asynchrony
Client-side asynchrony enables the request and response messages to be processed in two
different threads. There are two ways to implement this on the client side, described in
the following sections.
Nonblocking API
You can provide a nonblocking API to the client so it can hand over the request to the SOAP
engine and continue with the other work. There's a callback object the client provides
that's notified by the SOAP engine after the engine receives the response from the
server. This approach is especially useful for interactive applications with GUIs.
The Axis2 client API provides methods to be used for commonly used Message Exchange
Patterns (MEPs). Let's use the IN-OUT interaction to illustrate asynchronous support.
When you use the ServiceClient API, you use sendReceive(OMElement) or a variant of that method for synchronous invocations. However, sendReceiveNonBlocking(OMElement, Callback)
provides the first level of asynchronicity of the Axis2 client API, as shown in Listing 1.
Listing 1. Invoking an IN-OUT Web service without blocking the client
// First create the payload of the message.
OMElement payload = getPayload(); // add your payload here
// Create an instance of service client.
ServiceClient serviceClient = new ServiceClient();
// Create an options object to pass parameters to service client.
Options options = new Options();
// Set the location of the Web service.
options.setTo(new EndpointReference
("http://www.sample-web-service.com/StockQuote/getQuote"));
serviceClient.setOptions(options);
// Create a callback object.
Callback callback = new MyCallback();
// Do an async invocation.
serviceClient.sendReceiveNonBlocking(payload, callback);
// Continue doing other work without waiting until the response comes.
|
When the client invokes the Web service using the nonblocking API, it needs to pass a
callback object to the Axis2 ServiceClient API. Upon the
arrival of the response message, the Axis2 engine hands over this response message to the callback object. This callback object can then process this response on behalf of the client or hand it over to the client.
Now take a look at how the Callback should be implemented. The callback interface has
two methods:
- The
onComplete(AsyncResult) method, which is invoked
after the response is received
- The
AsyncResult object method, which encapsulates the resulting SOAP envelope and the message context
Clients can extend the Callback interface to handle the
response within this method. On the other hand, the onError(Exception) method is invoked in case of an error during the
invocation. Look at this in detail in Listing 2.
Listing 2. Implementing the Callback interface
class MyCallback extends Callback {
public void onComplete(AsyncResult result) {
// Get the response SOAP envelope from the result.
SOAPEnvelope envelope = result.getResponseEnvelope();
// Process SOAP envelope.
}
public void onError(Exception e) {
// Write here what you want to do in case of an error.
}
}
|
The Callback class can also be used as a polling mechanism.
Clients can continuously poll for a response by calling the Callback.isComplete() method. This is handy when there are multiple
Callback instances waiting for responses or if there's a
central controller that monitors for responses and updates the running application. So
the Callback class can be used both as a callback mechanism or as a polling mechanism during an asynchronous invocation.
Transport-level asynchrony
The second approach is to use transport-level asynchrony. All the transports that
can be used in a Web services invocation can be broadly categorized into two types
of invocations:
-
One-way transports provide a channel to either send or receive a
message.
-
Two-way transports can send and receive messages using the same channel.
Simple Mail Transfer Protocol (SMTP) is an example of one-way transport, and HTTP is a two-way transport. You
can do request-response interaction with a Web service using a single HTTP channel, but you need two channels if you're using SMTP.
A client has different options to use for a request-response interaction. The easiest
way is to use a two-way transport like HTTP. But this approach doesn't use
transport-level asynchrony. Clients can also use two one-way transports or two two-way transports. For example, you can use an SMTP channel to send the message
and another SMTP channel to receive the response message. At the same time, you can use
an HTTP channel to send the request, close it after the message has been sent, and use a
different HTTP channel to receive the response. But for this approach to work, there
should be a message correlation mechanism. Apache Axis2 uses the WS-Addressing
specification for message correlation. Here's how you can work with different transports.
First, send the request using any transport channel, and receive the response using a
different channel but with the same type of transport. The Axis2 client can use the
useSeparateListener flag to notify its intention to use a
separate listener to receive the response message. In other words, after this flag is
enabled, you're asking the Axis2 engine to use two different transport channels for this
invocation. The ServiceClient API, the easiest-to-use client
API within Axis2, has the method setUseSeparateListener(boolean) to set this value. Listing 3
demonstrates the use of the useSeparateListener API to invoke a Web service.
Listing 3. Using the useSeparateListener variable
try {
// first create the payload of the message
OMElement payload = getPayload(); // add your payload here
// create an instance of service client
ServiceClient serviceClient = new ServiceClient();
// create an options object to pass parameters to service client
Options options = new Options();
// set the location of the web service
options.setTo(new EndpointReference
("http://www.sample-web-service.com/StockQuote/getQuote"));
// inform Axis2 engine that you need to use a different channel for the response
options.setUseSeparateListener(true);
serviceClient.setOptions(options);
// invoke the service
serviceClient.sendReceive(getPayload());
} catch (AxisFault axisFault) {
axisFault.printStackTrace();
}
|
At this point, you ask the Axis2 engine to prepare a transport listener from the same
type of transport that's used to send the request; you also ask it to receive the
response. The proper transport being used is inferred by the endpoint reference value.
This example is trying to send the message to
http://www.sample-web-service.com/StockQuote/getQuote, and two HTTP transports are used
during this invocation. You can use either the sendReceive or sendReceiveNonBlocking method to invoke the Web service.
Now find out how to achieve server-side asynchrony using Axis2.
Server-side asynchrony
When a SOAP engine receives a SOAP request, the usual way is to process the request and
send the message in the same thread. But this might not always be useful. For example,
when it takes a considerable amount of time to process the request, the transport
channel might time out. Or sometimes holding resources for that duration might not be a
good option for a system that handles a lot of traffic. So the ideal option is to notify the client that the server received the message and send the reply later using a different transport channel.
Axis2 employs a message receiver that takes care of the MEP and hands over the message
to the request processing logic or the service implementation. During a synchronous
invocation, the message receiver invokes the proper method in the service implementation
class (assuming the service implementation is written in the Java™ language) and
then sends the response in the same thread. But during an asynchronous invocation, this
message receiver starts a new thread to invoke the service and send the notification to
the client. The new thread is responsible for sending the response to the client and
taking the response from the service implementation class. That's how server-side
asynchrony works in Axis2. Now check out how you can use it for your Web services.
There are two basic ways that you create your services:
- Write the Web Services Description Language (WSDL), then generate the code for the service implementation class and fill it up.
- Write the service implementation class, write the services.xml, and deploy it within Axis2.
Services.xml contains metadata about the Web service you're deploying. It lists the
service implementation classes, the methods exposed, and any parameters that should be passed to the Web service.
If you're starting with the WSDL-code-generation method described above, you can ask
the code generator to enable asynchronous invocations to your service. The code
generator then generates an asynchronous message receiver to invoke your service. When
you run a WSDL2Java script to generate Java code from a given WSDL file, use the -a option also to instruct the script to generate async code. If
you're using the second method, which is the code-first approach, then associate org.apache.axis2.async.AsyncMessageReceiver
for IN-OUT invocations within the services.xml. You have to associate the IN-OUT MEP to
org.apache.axis2.async.AsyncMessageReceiver,
as shown in Listing 4.
Listing 4.
Associating message receivers with the IN-OUT MEP variable
<messageReceivers>
// other message receivers
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class=
"org.apache.axis2.async.AsyncMessageReceiver"/>
</messageReceivers>
|
For the server-side asynchrony to work, you must use WS-Addressing, because you spawn a new thread to send the response, promising the client that the server will send the response later. But if request processing takes some time, then the transport that the client used might time out. So it might not be prudent to try to do asynchronous invocations in the same channel. When the client sends a request to an asynchronous Web service, it must also mention a valid replyTo endpoint for the server to send the response back.
From the client point of view, when it invokes this asynchronous
service—say using HTTP—it receives HTTP 202 in the same
transport channel. HTTP 202 tells the client that the request is accepted by the Web
service but that the processing has not been completed yet. Then the client receives the
response via the replyTo path put in the request message.
Conclusion
Asynchronous invocations provide a great deal of flexibility for Web services users and
for the people who write and host Web services. With more Web services becoming
generally available and getting more complex at the same time, more applications are
able to integrate those Web services into their applications. Because most of the final
applications ultimately interact with users, the challenge is to provide a better user
experience. Asynchronous Web services invocations provided in different levels, in both
the client and server sides, can help realize some of these modern challenges.
Resources Learn
Get products and technologies
- Innovate your next development project with
IBM trial software, available for download or on DVD.
Discuss
About the author  | 
|  | Eran Chinthaka is a member of Apache Software Foundation and a project
management committee member of the Apache Web services project. He is one of the
pioneering members of the Apache Axis2 and Apache Axiom projects. |
Rate this page
|