Introduction
I started with
this developerworks tutorial and its files.
It has a section
Asynchronous JAX-WS Web service invocations with SOAP over JMS, but the article is not doing any implementation, it is just a theoretical explanation. Let me show you how to call the web service asynchronously using soap over jms.
Software used:
- IBM Integration Designer 7.5.1
- IBM Process Server 7.5.1.0 on WAS 7.0.0.19
Service code generation
Generate your server code as described in the tutorial. Use "Top down EJB Web Service" to create a soap over jms service. The same service code is used for both sync and async calls.
Client code generation
Right click the wsdl file in RAD or WID, and select web services > generate client
Make sure you flag "enable asynchronous invocation for generated client"
Now in the client code, the *Async operations are important. Three methods are generated for each request reply operation in the wsdl.
This wsdl does not contain one way (fire and forget) operations, but if it did, it would result in one method in the client code. Because one way operations are alway async when using JMS, they have only one method in the generated class.
One method (without Async postfix) is for sync calling, one returns a response handler (for polling), and the third accepts a callbackhandler.
The standard synchronous method
public EchoStringResponse echoOperation(EchoStringInput parameter);
The asynchronous method with polling
public Response<EchoStringResponse> echoOperationAsync(EchoStringInput parameter);
The asynchronous method with callback
public Future<?> echoOperationAsync(EchoStringInput parameter, AsyncHandler<EchoStringResponse> asyncHandler);
Calling with response
Use the response wrapper to check if the async call is finished. While it is not finished, other tasks can be executed.
Response<EchoStringResponse> asyncresp = service.getEchoServicePort().echoOperationAsync(input);
while (!asyncresp.isDone()){
System.out.println("no response yet, let's have a nap");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("failed to nap :(");
}
}
try {
System.out.println("I got a response! request send: " + request.getParameter("input") + ", got response : " + asyncresp.get().getEchoResponse());
} catch (Exception e) {
e.printStackTrace();
}
Calling with callbackhandler
You can do the same with a callbackhandler, if it is not necessary to process the response in the same thread.
input.setEchoInput(input.getEchoInput() + " with callback");
service.getEchoServicePort().echoOperationAsync(input, new AsyncHandler<EchoStringResponse>() {
@Override
public void handleResponse(Response<EchoStringResponse> res) {
try {
System.out.println("I got a callback response! " + res.get().getEchoResponse());
} catch (Exception e) {
e.printStackTrace();
}
}});
Resources
This article in the infocenter talks about setting JVM properties for the async message listener, but I don't really understand what it is about, or why you should set these properties, it works fine without them, just define a reply queue:
http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.nd.multiplatform.doc/info/ae/ae/twbs_jmsasyncresplistener.html
More info on Invoking JAX-WS Web services asynchronously:
http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.base.doc/info/aes/ae/twbs_jaxwsclientasync.html
It makes the following statement:
To enable asynchronous mappings, you can add the jaxws:enableAsyncMapping binding declaration to the WSDL file.
I added that to the wsdl, and after I did the generated client will ALWAYS had the asynchronous method, even when I didn't check "enable asynchronous invocation for generated client" in the client generation wizard.
code:
<jaxws:bindings>
<jaxws:enableAsyncMapping>true</jaxws:enableAsyncMapping>
</jaxws:bindings>
Don't forget to add the namespace declaration to the wsdl
code:
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
If the server code is generated with the enableAsyncMapping extension added to the wsdl, you will find that also in the web service implementation class async methods have been generated
public Response<EchoStringResponse> echoOperationAsync(EchoStringInput parameter){
// TODO Auto-generated method stub
return null;
}
These are not valid at server side. You will get a stacktrace like this in the systemout :
[2/12/13 11:28:41:442 CET] 00000083 JMSListenerMD E WSWS3018E: Caught exception during request processing: WSWS3163E: Error: The Web services engine could not find a target service to invoke! targetService is EchoServicePortType
[2/12/13 11:28:42:173 CET] 0000007f SystemErr R java.util.concurrent.ExecutionException: javax.xml.ws.soap.SOAPFaultException: WSWS3163E: Error: The Web services engine could not find a target service to invoke! targetService is EchoServicePortType
[2/12/13 11:28:42:173 CET] 0000007f SystemErr R at org.apache.axis2.jaxws.client.async.AsyncResponse.onError(AsyncResponse.java:141)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr R at org.apache.axis2.jaxws.client.async.PollingFuture.onError(PollingFuture.java:95)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr R at org.apache.axis2.description.OutInAxisOperationClient$NonBlockingInvocationWorker.run(OutInAxisOperation.java:470)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr R at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:897)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr R at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:919)
[2/12/13 11:28:42:174 CET] 0000007f SystemErr R at java.lang.Thread.run(Thread.java:736)
You can remove these *Async postfixed methods in the implementation and interface to resolve this issue.