Web service invocation may be either synchronous or asynchronous.
In synchronous invocation, a client sends a request to a service and then halts operation while waiting for a service response. Due to this simplicity, Web service invocation is synchronous by default. Web Service Clients deals with synchronous invocation.
However, synchronous invocation is not suitable when the service could take significant time to perform. In such a case, the client would wait until it eventually received the response, blocking other processes and wasting system resources, maintaining an unused transport channel. In these cases, asynchronous invocation must be used.
Using WSO2 SOA Enablement Server asynchronous invocation, the client side becomes fully event driven, efficiently using system resources, processing service responses once it is ready.
When invoking a service asynchronously, the message is processed first by client handlers and interceptors, after which it is put into the client message queue and control is returned to the client so it can continue with further processing. There is a special pool of threads in WSO2 SOA Enablement Server called senders, which manage this message queue.
Each message is given an id which is used to correlate with the response from the server. Once a sender is ready, a message is sent to the server.
By default, the response is received by the aforementioned senders pool. However, an alternative exists for asynchronous invocation. This is the feature called transport coupling.
In transport coupling, a special receiver service endpoint is enabled on the client and all responses from the server are routed to it. The advantage of this architecture is that the connection between client and server is not blocked. Instead, a new connection (and possibly another transport) is used for response. The Transport Coupling demo demonstrates this feature. In this demo the client sends the request to the server via mail transport and listens for the response on http transport.
Although transport coupling saves system resources, it is not suitable in some cases involving firewalls and lightweight clients. In the first case, firewalls are usually configured to throw away unsolicited responses. In the second case, a lightweight client may not have the resources to run its own transport to the host receiver service endpoint. See Configuring Coupled Transports to know how to set up coupled transports in WSO2 SOA Enablement Server.
Once the response is ready on the client (regardless of which receiver mechanism was used), it is put in the message queue and results can be obtained.
There are two possible mechanisms for getting the results of asynchronous invocation: polling and callbacks. WSO2 SOA Enablement Server provides an API for both approaches, although callbacks are more Java-like. Both mechanisms work independently of the type and level of client (see Invocation Overview).
Callback mechanism
The callback mechanism is java-like, using client listeners, referred to as callbacks. These callbacks are notified by the client side when the results to an asynchronous invocation are ready (in the message queue).
Polling mechanism
In general polling means that the client periodically checks for a response to a given asynchronous invocation. WSO2 SOA Enablement Server's implementation checks the message queue for a response, and so it does not require further communication with the server.
Using the WSO2 SOA Enablement Server asynchronous API, one can invoke any currently published Web service. No special measures are required on the server side to cater for asynchronous clients.
WSO2 SOA Enablement Server asynchronous invocation is based on begin- and end- methods. This model applies at both the application level (dynamically created proxy or JAX-RPC API) and the XML/SOAP protocol level (JAXM API). This means that the asynchronous framework works the same way regardless of the client type (JAX-RPC, JAXM, etc. See Invocation Overview). Here we use a JAX-RPC Java application level client as an example.
Table 3. A Call Method Split into begin- and end- Methods
Method type | Purpose |
---|---|
method (for example, call()) | Calls the Web service synchronously |
begin method (for example, beginCall()) | Starts asynchronous communication with the Web service method |
end method (for example, endCall()) | Gets the result of the asynchronous call to the Web service method |
Every begin method returns org.systinet.wasp.async.AsyncConversation, which acts as a handle for an asynchronous invocation once it has started. Using this interface, the client can obtain information about the invocation state or register the callback that will get called once the response comes asynchronously from the service. It has the following signature:
abstract class org.systinet.wasp.async.AsyncConversation { public void setCallback(AsyncCallback callback); public boolean isResponseReady(); public void setTimeout(long millis); public abstract long getTimeout(); public abstract void finish(); ...}
The client can register a callback using AsyncConversation.setCallback(AsyncCallback callback). The callback has the following signature:
interface org.systinet.wasp.async.AsyncCallback { void onResponse(AsyncConversation async); void onTimeout(AsyncConversation asyncConversion); ...}
WSO2 SOA Enablement Server provides a generic implementation of the AsyncCallback interface called org.systinet.wasp.async.GenericAsyncCallback. Every asynchronous callback can have a time-out set but there are situations when you may not want to deal with this. In this case, GenericAsyncCallback is especially useful as it provides a generic implementation of the onTimeout(...) method. Examples of using this API are in the following sections.
For a WSO2 SOA Enablement Server client to communicate with a Web service asynchronously, begin- and end- methods must be present for every synchronous method in the interface used for generating the service proxy.
The WSDL2Java tool automatically generates the two asynchronous methods (begin- and end-) for every synchronous method. This feature gives you the freedom to choose whether to call the Web service synchronously or asynchronously.
You can disable this feature using the switch -l, --output-language (attribute outputLanguage in the WSDL2Java ANT task). When using the WSDL2Java API, use the method setOutputLanguage(). The default value for the language, java-async, generates Java interfaces with asynchronous methods. If you do not want the asynchronous methods to be generated, set the value for the language to java instead.
An example of an interface with asynchronous methods is shown here:
String echoString(String s); org.systinet.wasp.async.AsyncConversation beginEchoString(Strings); String endEchoString(org.systinet.wasp.async.AsyncConversation);
Here the synchronous method echoString() has been split into two methods with the prefixes 'begin' and 'end.' The arguments of the 'begin' method are the same as those of the synchronous method, but the return type is always org.systinet.wasp.async.AsyncConversation. This result is used later in the 'end' method to obtain the result of the call the first argument of the 'end' method is always org.systinet.wasp.async.AsyncConversation. The rest of the 'end' method arguments are the [out] and [in/out] parameters of the synchronous method, if any. (Please see In/Out Parameters) The return type is the same as the return type of the synchronous method. If the generated 'end-' method would clash with other methods already present in the class (for example, methods with the same arguments but with a different return type), the character(s) '0'..'9' are inserted right after the 'begin' and 'end' prefixes. The 'begin' method always has the same arguments as the original synchronous method.
Example 95. Asynchrony with Java Dynamic Proxy
package example.advanced.invocation.async; import org.systinet.wasp.Wasp; import org.systinet.wasp.async.AsyncConversation; import org.systinet.wasp.async.GenericAsyncCallback; import org.systinet.wasp.webservice.Registry; import org.systinet.wasp.webservice.ServiceClient; public class ProxyAsync extends GenericAsyncCallback { private static final String SERVER_URL = "http://localhost:6060"; private static final String SERVICE_PATH = "/HelloWorld"; private static final String WSDL_PATH = SERVER_URL + SERVICE_PATH + "/wsdl"; //client configuration private ServiceClient serviceClient; //proxy object private HelloService proxy; //start server and publish service private void setUp() throws Exception { Wasp.startServer(SERVER_URL); Registry.publish( SERVICE_PATH, new HelloServiceImpl()); System.out.println("Server started. Service published."); } //asynchronously call service private void doAsyncInvocation() throws Exception { serviceClient = ServiceClient.create(WSDL_PATH); proxy = (HelloService) serviceClient.createProxy(HelloService.class); AsyncConversation async = proxy.beginHello("Hello from proxy!"); async.setCallback(this); } //implementation of AsyncCallback public void onResponse(AsyncConversation async) { try { String response = proxy.endHello(async); System.out.println("Response received: " + response); } finally { //shutdown WASP when response received async.finish(); tearDown(); } } //shutdown WASP private void tearDown() { Wasp.destroy(); } public static void main(String[] args) throws Exception { ProxyAsync proxyAsync = new ProxyAsync(); proxyAsync.setUp(); proxyAsync.doAsyncInvocation(); } }
The asynchronous API for a Java-level JAX-RPC client is the class org.systinet.wasp.rpc.WaspCall , which is an extension of javax.xml.rpc.Call. Depending on your client type, an instance of this class can be obtained either the pure JAX-RPC way:
... ServiceFactory factory = ServiceFactory.newInstance(); Service service = factory.createService (new URL(http://localhost:6060/HelloWorld), new QName(http://my.org,HelloWorldService)); WaspCall waspCall = (WaspCall)service.createCall(); ...
...or by using the following WSO2 SOA Enablement Server runtime API method:
... ServiceClient client = ServiceClient.create(http://localhost:6060/HelloWorld); WaspCall waspCall = (WaspCall)client.createCall(); ...
Use the latter method of creating a WaspCall instance when the client side must be configured further (interceptors, etc.).
Class org.systinet.wasp.rpc.WaspCall has the following methods for asynchronous invocation:
beginInvoke(java.lang.Object[] inputParams)
beginInvoke(QName operationName, java.lang.Object[] inputParams) throws RemoteException;
endInvoke(AsyncConversation asyncResult)
getOutputValues(AsyncConversation asyncResult)
getOutputParams(AsyncConversation asyncResult)
Example 96. Asynchrony with Java JAX-RPC
package example.advanced.invocation.async; import org.systinet.wasp.Wasp; import org.systinet.wasp.async.AsyncConversation; import org.systinet.wasp.async.GenericAsyncCallback; import org.systinet.wasp.rpc.WaspCall; import org.systinet.wasp.webservice.Registry; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.rpc.Service; import javax.xml.rpc.ServiceFactory; public class JAXRPCAsync extends GenericAsyncCallback { private static final String SERVER_URL = "http://localhost:6060"; private static final String HELLO_URI = "/HelloWorld"; private static final QName HELLO_SERVICE_QNAME = new QName("http://wso2.com/wsdl/example/advanced/invocation/async/", "HelloServiceImpl"); private static final QName HELLO_OPERATION_QNAME = new QName("http://wso2.com/wsdl/example/advanced/invocation/async/", "hello"); private WaspCall asyncCall; private void setUp() throws Exception { Wasp.startServer(SERVER_URL); Registry.publish( HELLO_URI, new HelloServiceImpl()); } private void doAsyncInvocation() throws Exception { setUp(); Service service = ServiceFactory.newInstance() .createService( new URL(SERVER_URL + HELLO_URI), HELLO_SERVICE_QNAME); asyncCall = (WaspCall) service.createCall(); AsyncConversation async = asyncCall.beginInvoke( HELLO_OPERATION_QNAME, new Object[] { "JAX-RPC async client" }); async.setCallback(this); } private void tearDown() { Wasp.destroy(); } public void onResponse(AsyncConversation async) { try { Object res = asyncCall.endInvoke(async); System.out.println("Response received:" + res); } catch (java.rmi.RemoteException e) { e.printStackTrace(); } finally { async.finish(); tearDown(); } } public static void main(String[] args) throws Exception { JAXRPCAsync asyncClient = new JAXRPCAsync(); asyncClient.doAsyncInvocation(); } }
![]() | Important |
---|---|
It is not possible to reuse org.systinet.wasp.rpc.WaspCall for multiple asynchronous calls. The user must create a new WaspCall instance for each asynchronous invocation as shown in Example 97. |
Example 97. Making Multiple Calls
... AsyncConversation[] acs = new AsyncConversation[count]; WaspCall[] call = new WaspCall[count]; for (int i = 0; i < count; i++) { // new WaspCall must be created for each async invocation call[i] = (WaspCall) service.createCall(); acs[i] = call[i].beginInvoke(HELLO_OPERATION_QNAME, new Object[]{ "Invocation number:" + i}); } ...
The asynchronous API on the XML messaging level is located in the class org.systinet.wasp.messaging.WaspProviderConnection , which is an extension of javax.xml.messaging.ProviderConnection. Instantiate this class as follows:
... ProviderConnectionFactory factory = ProviderConnectionFactory.newInstance(); WaspProviderConnection = (WaspProviderConnection)factory.createConnection; ...
org.systinet.wasp.messaging.WaspProviderConnection has the following asynchronous API:
Example 98 shows an example of a class using this API:
Example 98. Asynchrony on the XML/SOAP Protocol Level Using JAXM
package example.advanced.invocation.async; import org.systinet.wasp.Wasp; import org.systinet.wasp.async.AsyncConversation; import org.systinet.wasp.async.GenericAsyncCallback; import org.systinet.wasp.messaging.WaspProviderConnection; import org.systinet.wasp.soap.WaspSOAPMessage; import org.systinet.wasp.webservice.Registry; import java.util.Iterator; import javax.xml.messaging.JAXMException; import javax.xml.messaging.ProviderConnectionFactory; import javax.xml.soap.MessageFactory; import javax.xml.soap.Name; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import javax.xml.soap.SOAPPart; public class JAXMAsync extends GenericAsyncCallback { private static final String SERVER_URL = "http://localhost:6060"; private static final String HELLO_URI = "/HelloWorld"; private static final String SOAP_ACTION = "http://wso2.com/wsdl/example/advanced/invocation/async/" + "HelloServiceImpl#hello#KExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9s" + "YW5nL1N0cmluZzs="; private WaspProviderConnection waspConnection; private void setUp() throws Exception { Wasp.startServer(SERVER_URL); Registry.publish( HELLO_URI, new HelloServiceImpl()); } private void doAsyncInvocation() throws Exception { setUp(); ProviderConnectionFactory factory = ProviderConnectionFactory.newInstance(); waspConnection = (WaspProviderConnection) factory.createConnection(); SOAPMessage request = createHelloMessage(); AsyncConversation async = waspConnection.beginCall(request, SERVER_URL + HELLO_URI); async.setCallback(this); } private void tearDown() { Wasp.destroy(); } public void onResponse(AsyncConversation async) { try { SOAPMessage response = waspConnection.endCall(async); System.out.println("Response received:" + getResponse(response)); } catch (JAXMException e) { e.printStackTrace(); } finally { async.finish(); tearDown(); } } private String getResponse(SOAPMessage message) { SOAPBody body = null; try { body = message.getSOAPPart() .getEnvelope() .getBody(); } catch (SOAPException e) { e.printStackTrace(); return null; } Iterator it = body.getChildElements(); SOAPElement retval = null; while (it.hasNext()) { Object obj = it.next(); if (obj instanceof SOAPElement) { retval = (SOAPElement) obj; break; } } if (retval == null) { System.out.println("No return value!"); return null; } return retval.getValue(); } private SOAPMessage createHelloMessage() throws Exception { SOAPMessage message = MessageFactory.newInstance() .createMessage(); SOAPPart sp = message.getSOAPPart(); SOAPEnvelope env = sp.getEnvelope(); SOAPBody body = env.getBody(); Name name = env.createName("p0", null, "http://wso2.com/xsd/SchemaTypes"); SOAPElement el = body.addBodyElement(name); body.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema"); el.addAttribute( env.createName("type", "xsi", "http://www.w3.org/2001/XMLSchema-instance"), "xsd:string"); el.addTextNode("JAXM async client"); ((WaspSOAPMessage) message).setSOAPAction(SOAP_ACTION); return message; } public static void main(String[] args) throws Exception { JAXMAsync asyncClient = new JAXMAsync(); asyncClient.doAsyncInvocation(); } }
All the examples given above use the callback mechanism. Example 99 demonstrates the use of polling in asynchronous invocations. Proxy level invocation is shown but the API is the same on all invocation levels, as is the case when using the callback mechanism.
Example 99. Proxy-Level Invocation Using the Polling Mechanism
package example.advanced.invocation.async; import org.systinet.wasp.Wasp; import org.systinet.wasp.async.AsyncConversation; import org.systinet.wasp.webservice.Registry; import org.systinet.wasp.webservice.ServiceClient; public class PollingProxyAsync { private static final String SERVER_URL = "http://localhost:6060"; private static final String SERVICE_PATH = "/HelloWorld"; private static final String WSDL_PATH = SERVER_URL + SERVICE_PATH + "/wsdl"; //client configuration private ServiceClient serviceClient; //proxy object private HelloService proxy; //start server and publish service private void setUp() throws Exception { Wasp.startServer(SERVER_URL); Registry.publish( SERVICE_PATH, new HelloServiceImpl()); System.out.println("Server started. Service published."); } //asynchronously call service private void doAsyncInvocation() throws Exception { serviceClient = ServiceClient.create(WSDL_PATH); proxy = (HelloService) serviceClient.createProxy(HelloService.class); AsyncConversation async = proxy.beginHello("Hello from proxy!"); while (!async.isResponseReady()) { System.out.println("Doing something else"); Thread.sleep(50); } String response = proxy.endHello(async); async.finish(); System.out.println("Response received: " + response); tearDown(); } //shutdown WASP private void tearDown() { Wasp.destroy(); } public static void main(String[] args) throws Exception { PollingProxyAsync proxyAsync = new PollingProxyAsync(); proxyAsync.setUp(); proxyAsync.doAsyncInvocation(); } }
Using coupled transports, or transport coupling, means having different transport channels for the request and response messages, such as an HTTP request and an email response, or even an email request and an email response (because email does not inherently provide a single request/response channel).
Starting in WSO2 SOA Enablement Server for Java version 5.5, transport coupling uses the WS-Addressing protocol. The older WS-Routing protocol is no longer supported.
![]() | Important |
---|---|
Transport coupling involving a client or server older than version 5.5 must be made backward compatible due to the change in protocols. Please see Transport Coupling in Asynchronous Invocations for details. |
With transport coupling, the connection between server and client is not blocked, which saves system resources. It is sometimes not possible to use coupled transports, however, due to firewall restrictions or overly lightweight clients.
There are two ways to have WSO2 SOA Enablement Server use a different transport channel for response:
via Runtime Publishing API
via client deployment descriptor
Via Runtime Publishing API, asynchronous transport is customized by the following methods on org.systinet.wasp.webservice.ServiceClient :
setAsyncTransport(String scheme)
Example 100 shows a client demonstrating the use of this API.
Example 100. Client With Asynchronous Transport
package example.advanced.invocation.async; import org.systinet.wasp.async.AsyncConversation; import org.systinet.wasp.webservice.ServiceClient; public class TransportCouplingClient { private static final String WSDL_URL = "http://localhost:6060/HelloWorld"; public static void main(String[] args) throws Throwable { ServiceClient serviceClient = ServiceClient.create(WSDL_URL); serviceClient.setAsyncTransport("mailto"); HelloService proxy = (HelloService) serviceClient.createProxy(HelloService.class); AsyncConversation async = proxy.beginHello("Hello from proxy!"); String response = proxy.endHello(async); System.out.println("Response received: " + response); } }
Example 101 shows a client deployment descriptor setting asynchronous protocol. This descriptor is than packed to the client deployment package (See WASPPackager), which must be located in the classpath during invocation.
Example 101. Deployment Descriptor Setting Asynchronous Transport Protocol
<?xml version="1.0" encoding="UTF-8"?> <package client-package="true" name="RoutingHelloClient" targetNamespace="http://wso2.com/examples/async/" version="1.0" xmlns="http://wso2.com/wasp/package/1.0" xmlns:tns="http://wso2.com/examples/async/"> <license>http://wso2.com</license> <client-processing async-protocol="mailto" name="TransportCoupling"/> <service-client client-processing="tns:TransportCoupling" uri="http://localhost:6060/HelloWorld/"/> </package>
See also the Transport Coupling demo.