Skip to main content

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

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

All information submitted is secure.

  • Close [x]

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.

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

All information submitted is secure.

  • Close [x]

Write reliable clients for SOAP/HTTP Web services

Shailesh K. Mishra (shailekm@in.ibm.com), Software Engineer, IBM
Shailesh Mishra photo
Shailesh K. Mishra is Software Engineer at the IBM Software Lab in Gurgaon, India. He is currently working on the Business Portlets project. His main area of interest is business integration.

Summary:  Learn how to write reliable clients for SOAP/HTTP Web services using a SOAP message handler to read and cache the SOAP body. This method saves data used in preparation of a Web service call in case of events such as server failure or network service failure. Once the client gets a response from the Web service, it can discard this data from cache, otherwise it uses same data to invoke the Web service again.

Date:  10 Nov 2006
Level:  Intermediate
Also available in:   Chinese  Russian  Japanese

Activity:  6686 views
Comments:  

Introduction

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:

  1. Open the web.xml file and go to the Handlers tab, shown in Figure 1.


    Figure 1. Handlers tab


  2. Click on Add and fill in the blank fields with required values, as shown in Figure 2.


    Figure 2. Fill in message-handler details


  3. 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.


Conclusion

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.



Downloads

DescriptionNameSizeDownload method
Source code for samplessourcesamples.zip33KBHTTP
Runtime descriptionruntime.txt1KBHTTP

Information about download methods


Resources

Learn

Discuss

About the author

Shailesh Mishra photo

Shailesh K. Mishra is Software Engineer at the IBM Software Lab in Gurgaon, India. He is currently working on the Business Portlets project. His main area of interest is business integration.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


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. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

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.

(Must be between 3 – 31 characters.)

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

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services
ArticleID=173280
ArticleTitle=Write reliable clients for SOAP/HTTP Web services
publish-date=11102006
author1-email=shailekm@in.ibm.com
author1-email-cc=