In a normal Web service invocation scenario, a Web service client prepares the call and invokes the Web service. If temporary system errors, network failures, or service unavailability occur, the data used in preparation of the call is lost. You can save this data in a number of ways. One way is to use a SOAP message handler (hereafter I'll also call them handlers), even though handlers are used most commonly for SOAP header processing. SOAP headers are used to carry contextual data for a request, for example Quality of Service (QoS) requests such as security and transactionality. In these cases you can use handlers to read the SOAP body. This article will show you how to use handlers to cache the body, use this cached data in case of failure conditions, and then write a reliable Web service client.
Write a Web service message handler
The main reason to develop a message handler is to save the data used in preparing the call to invoke the Web service. Listing 1 shows a message handler that reads the request body while the request is being sent.
Listing 1. Message-handler code
package com.ibm.reliablewsclient.ws;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.GenericHandler;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
/*
* Created on Aug 3, 2006 @author Shailesh K Mishra (shailekm@in.ibm.com)
*
*/
public class ClientHandler extends GenericHandler {
private Logger logger;
public static SOAPBody body_of_request=null;
/**
*
*/
public ClientHandler() {
super();
// TODO Auto-generated constructor stub
}
/*
* (non-Javadoc)
*
* @see javax.xml.rpc.handler.Handler#init(javax.xml.rpc.handler.HandlerInfo)
*/
public void init(HandlerInfo arg0) {
// set up logger
logger = Logger.getLogger("com.ibm.reliablewsclient.ws");
super.init(arg0);
}
/*
* (non-Javadoc)
*
* @see javax.xml.rpc.handler.Handler#getHeaders()
*/
public QName[] getHeaders() {
// TODO Auto-generated method stub
return null;
}
/*
* (non-Javadoc)
*
* @see javax.xml.rpc.handler.Handler#handleRequest(javax.xml.rpc.handler.MessageContext)
*/
public boolean handleRequest(MessageContext arg0) {
try {
logger.info("Begin procession ClientHandler.handleRequest");
//generate SOAP body
SOAPMessage message = ((SOAPMessageContext) arg0).getMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
body_of_request=body;
logger.info("Request Body : " + body.toString());
logger.info("Completed procesing for ClientHandler.handleRequest");
} catch (Throwable ex) {
throw new JAXRPCException("Error in handleRequest", ex);
}
return true;
}
/*
* (non-Javadoc)
*
* @see javax.xml.rpc.handler.Handler#handleResponse(javax.xml.rpc.handler.MessageContext)
*/
public boolean handleResponse(MessageContext arg0) {
return true;
}
}
|
The handler code in Listing 1 reads the message body and assigns it to a static variable. The purpose of this static body_of_request field is to cache the data used in preparing the Web service invocation. This is a very simple, but not scalable, caching technique.
Write a reliable managed Web service client
A managed client is one that runs in a managed environment; that is, it functions through an application server. I'll demonstrate how to write a reliable managed client using a servlet as the client. First, you need to write a Web service and then generate the stubs for that Web service. To generate the stubs, right-click on Web service's WSDL file in your development environment (I've used IBM Rational ® Application Developer™), then click on the generate-client option. Once stubs are generated, you're ready to write the servlet. Listing 2 shows the servlet's doGet method.
Listing 2. The servlet's doGet method
try {
DemoWSProxy proxy=new DemoWSProxy();
String str=proxy.generateId("John",2834742,"IBM Bangalore");
throw new RemoteException("To demostrate");
} catch (RemoteException e) {
try {
SOAPConnectionFactory fact;
fact = SOAPConnectionFactory.newInstance();
SOAPConnection con = fact.createConnection();
javax.xml.soap.SOAPFactory sf = SOAPFactory.newInstance();
MessageFactory mfact = MessageFactory.newInstance();
SOAPMessage smsg = mfact.createMessage();
SOAPPart prt = smsg.getSOAPPart();
SOAPEnvelope env = prt.getEnvelope();
env.addChildElement(ClientHandler.body_of_request);
//Set the WebService end point URL
URL endpoint = new URL("http://localhost:9080/
ReliableWSClientProject/services/DemoWS");
//Send the message
SOAPMessage response = con.call(smsg, endpoint);
System.out.println(response.getSOAPBody().toString());
response.writeTo(arg1.getOutputStream());
System.out.println();
//Close the connection
con.close();
} catch (UnsupportedOperationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SOAPException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
|
When the doGet method is invoked, it instantiates the WSProxy class (created during stubs generation) and invokes the Web service's generateId method. For demonstration purposes, I've thrown a RemoteException to show the use of cached data. Whenever the Web service is invoked, a message handler reads the body of the request and saves the whole body in the body_of_request static variable. Now when the control reaches the throw RemoteException, a remote exception will be thrown and the flow will enter into the catch block. In this catch block, the saved request body is used to prepare a SOAP request to invoke the Web service again.
Configure the message handler with a servlet
To invoke this handler when this servlet invokes the Web service, you need to configure the handler by performing the following steps:
- Open the web.xml file and go to the Handlers tab, shown in Figure 1.
Figure 1. Handlers tab
- Click on Add and fill in the blank fields with required values, as shown in Figure 2.
Figure 2. Fill in message-handler details
- Click Finish and save the web.xml file.
Your message handler is now configured.
Write a reliable nonmanaged Web service client
A nonmanaged client is one that runs in a nonmanaged environment, for example a stand-alone Java™ client. For such clients, you need to configure the handlers programmatically. Listing 3 shows reliable nonmanaged client code.
Listing 3. Nonmanaged client
public class MyWSInvoker {
/**
*
*/
public MyWSInvoker() {
super();
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
try {
ArrayList handlerList = new ArrayList();
//Instantiate HandlerInfo class by passing your MessageHandler
// class and put this
//HandlerInfo class into an arraylist
handlerList.add(new HandlerInfo(ClientHandler.class, null, null));
ServiceFactory fact = ServiceFactory.newInstance();
Service service = fact.createService(new QName(
"http://ws.reliablewsclient.ibm.com", "DemoWSService"));
HandlerRegistry handlerRegistry = service.getHandlerRegistry();
//QName passed in setHandlerChain method should be QName of
// PortType
handlerRegistry.setHandlerChain(new QName(
"http://ws.reliablewsclient.ibm.com", "DemoWS"),
handlerList);
Call call = service.createCall();
call.setPortTypeName(new QName(
"http://ws.reliablewsclient.ibm.com", "DemoWS"));
call.setOperationName(new QName(
"http://ws.reliablewsclient.ibm.com", "generateId"));
call.setTargetEndpointAddress("http://localhost:9080/
ReliableWSClientProject/services/DemoWS");
call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema","string"));
Object obj = call.invoke(new Object[] { "Jerry",new Integer(1234), "BIM" });
if (obj instanceof String) {
System.out.println((String) obj);
}
throw new RemoteException("my remote");
} catch (RemoteException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
try {
Thread.sleep(10000);
SOAPConnectionFactory fact;
fact = SOAPConnectionFactory.newInstance();
SOAPConnection con = fact.createConnection();
javax.xml.soap.SOAPFactory sf = SOAPFactory.newInstance();
MessageFactory mfact = MessageFactory.newInstance();
SOAPMessage smsg = mfact.createMessage();
SOAPPart prt = smsg.getSOAPPart();
SOAPEnvelope env = prt.getEnvelope();
env.addChildElement(ClientHandler.body_of_request);
//Set the WebService end point URL
URL endpoint = new URL(
"http://localhost:9080/ReliableWSClientProject/services/DemoWS");
//Send the message
SOAPMessage response = con.call(smsg, endpoint);
System.out.println(response.getSOAPBody().toString());
response.writeTo(System.out);
System.out.println();
//Close the connection
con.close();
} catch (InterruptedException e1) {
//TODO Auto-generated catch block
e1.printStackTrace();
} catch (UnsupportedOperationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SOAPException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} catch (ServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
To configure a message handler in this case, instantiate a HandlerInfo by passing your message-handler class name as:
new HandlerInfo(ClientHandler.class, null, null);
|
Then add this HandlerInfo class into an array list:
ArrayList handlerList = new ArrayList();
handlerList.add(new HandlerInfo(ClientHandler.class, null, null));
|
Now get the HandlerRegistry:
HandlerRegistry handlerRegistry = service.getHandlerRegistry();
|
Register your handler in HandlerRegistry:
handlerRegistry.setHandlerChain(
new QName("http://ws.reliablewsclient.ibm.com", "DemoWS"),handlerList);
|
Now the message handler has been configured. Whenever a request is sent to a Web service, this handler reads the SOAP body and assigns it to the body_of_request static field.
Listing 3 throws a RemoteExpection to demonstrate use of the cached body. When the control reaches the catch block, a SOAP request is prepared using a cached body, and a Web service is invoked.
In this article you learned the simple steps required to use message handlers to write reliable managed and nonmanaged Web service clients. Using a static field to cache the request body isn't a sophisticated and scalable technique, but you can use any good mechanism for caching.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for samples | sourcesamples.zip | 33KB | HTTP |
| Runtime description | runtime.txt | 1KB | HTTP |
Information about download methods
Learn
-
The IBM Redbook WebSphere Version 6 Web Services Handbook Development and Deployment presents the underlying concepts, architectures, and specifications for the use of Web services; shows how Web services can be implemented and deployed using the latest IBM products; and covers some advanced techniques.
-
"FAWS for SOAP-based Web services" (developerWorks, January 2005) presents a portable system that provides client-transparent fault tolerance for SOAP-based Web services.
-
Stay current with developerWorks technical events and webcasts.
-
The IBM SOA Web site offers an overview of SOA and how IBM can help you to get there.
-
Visit the SOA and Web services zone on developerWorks to learn more about SOA.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.
-
Collaborate with a community of architects and developers in the SOA and Web services discussion forums.





