Web Services Processing Request/Response Messages of a Web Service Using Handler Chain

Processing Request/Response Messages of a Web Service Using Handler Chain

Web Services are all about processing messages. Handlers are classes that act as pre-processors and post-processors of these messages. A series of Handlers can be invoked in some order. This series of Handlers is called a Handler Chain. The order of invocation is determined by deployment configuration and also whether it’s on the client or server side.

Why Use Handlers?

A Handler can be used to modify a SOAP request or response message on the client and the server side. A simple example of using handlers is to encrypt and decrypt secure data in the body of a SOAP message. A client application uses a handler to encrypt the data before it sends the SOAP message request to the Web service. The Web service receives the request and uses a handler to decrypt the data before it sends the data to the back-end component that implements the Web service. The same steps happen in reverse for the response SOAP message.

Another example can be accessing information in the header part of the SOAP message. A SOAP header can be used to store Web Service-specific information and then use handlers to manipulate it. A specific example could be sending the username and password in a SOAP header to an Authentication Handler.

SOAP message handlers also can be used to improve the performance of the Web service. This can be achieved by caching the SOAP response of the frequently used queries. Handlers also can be used to validate the SOAP request and also to log the interactions.

Handler Chain

A Handler is assembled into a chain of handlers known as a Handler Chain. An implementation of a Web Service might require several handler classes to be invoked, one after or before the request/response is served. The JAX-RPC runtime is capable of invoking several handler classes in a chain. This concept is called handler chain.

Handler Chain Model

A Handler class is tied to the service endpoint and can be configured to intercept the SOAP message and perform various operations on it at any of the following points in the client-service communication:

  1. At the client, just after the SOAP request is created and before it is sent to the Web Service.
  2. At the Web Service end, before the SOAP request is processed by the Web Service.
  3. At the Web Service end, before sending the SOAP response to the client.
  4. At the client, just before the JAX-RPC runtime processes the SOAP response received from the Web Service.

A Handler chain can be used with or without a back end component. The following section deals with the different cases of a Handler Chain.

Figure 1: Handler Chain Model with Back End Component

Figure 1 shows the execution flow of the Handler chain with a back end component. When the client application invokes the Web Service, the SOAP request reaches the handleRequest() of the handler in the handler chain. Once processed, the control is transferred to the Web Service and the Web Service is executed. The handleResponse() of the handler is invoked from the Web Service and the SOAP response is sent to the client.

Figure 2: Handler Chain Model with Back End Component

Example

The example assumes that the underlying server is Weblogic 8.x. This section explains about the Authentication Handler (example) with a back end component as a Balance Enquiry Service. The authentication details here are the account number and the pin, which are set in the SOAP header of the request message. The Authentication Handler retrieves the account number and pin and authenticate them. Once authenticated, the back end component BalanceEnquiry is invoked to get the balance of the account number provided.

Following are the steps for writing a Handler.

  1. The handler class should implement the interface javax.xml.rpc.handler.Handler or extend the GenericHandler class of Weblogic(weblogic.webservice.GenericHandler).
  2. The javax.xml.rpc.handler.Handler interface contains the following methods that are to be implemented:
    • init ()
    • destroy()
    • getHeaders()
    • handleRequest()
    • handleResponse()
    • handleFault()

If weblogic.webservice.GenericHandler is used, override only the necessary methods. In this example, the AuthenticationHandler overrides the handleRequest() and handleResponse() methods. If the handleRequest() method returns true, the next handler in the chain will be invoked. If the handler is the last component in the chain, the back end component (Web Service) will be invoked. In case it returns false, the handleResponse() of that corresponding handler will be invoked.

The AuthenticationHandler’s handleRequest() retrieves the account number and pin from the SOAP header and authenticates it and then returns true or false based on the result.

AuthenticationHandler.java

import java.util.Map;import java.util.Iterator;import javax.xml.rpc.JAXRPCException;import javax.xml.rpc.handler.soap.SOAPMessageContext;import javax.xml.rpc.handler.HandlerInfo;import javax.xml.rpc.handler.MessageContext;import javax.xml.namespace.QName;import javax.xml.soap.*;import weblogic.webservice.GenericHandler;public class AuthenticationHandler extends GenericHandler{   private int me = System.identityHashCode(this);   private HandlerInfo handlerInfo = null;   String accountNo                = null;   String pin                      = null;   public void init(HandlerInfo handlerInfo)   {      this.handlerInfo = handlerInfo;   }   public boolean handleRequest(MessageContext messageContext)   {      System.err.println("** handleRequest called in: "+me);      try      {         SOAPMessageContext sctx = (SOAPMessageContext)messageContext;         SOAPMessage message     = sctx.getMessage();         SOAPPart sp             = message.getSOAPPart();         SOAPEnvelope senv       = sp.getEnvelope();         SOAPHeader sh           = senv.getHeader();         Iterator iter =  sh.getChildElements();         if(iter.hasNext())         {            iter.next();                 //skip text            Object obj = iter.next();    //account details            SOAPElement elt = (SOAPElement)obj;            iter = elt.getChildElements();            iter.next();                 //skip text node            obj = iter.next();            elt = (SOAPElement)obj;            accountNo = elt.getValue();            iter.next();                 //skip text node            obj = iter.next();            elt = (SOAPElement)obj;            pin = elt.getValue();         }      }      catch (Exception e)      {         e.printStackTrace();         throw new JAXRPCException(e);      }      if(accountNo.equals("12345") && pin.equals("6789"))         return true;      else         return false;   }   public boolean handleResponse(MessageContext messageContext)   {      System.out.println("Inside HandleResponse");      return true;   }   public QName[] getHeaders()   {      return handlerInfo.getHeaders();   }}

The following is the code for the Balance Enquiry service. It is a very simple service that returns the balance amount of the given account number.

BalanceEnquiryService.java

public class BalanceEnquiryService{   public float getBalance(String accountNo)   {      if(accountNo.equals("12345"))         return 5000f;      else         return 100f;   }}

The web-service.xml of the Web Service is to be modified to include the handler chain details. The following are the changes to be done in web-services.xml:

  1. Create a <handler-chains> child element of the <web-services> root element that will contain a list of all handler chains defined for the Web service.
  2. Create a <handler-chain> child element of the <handler-chains> element; within this element, list all the handlers in the handler chain. For each handler, use the class-name attribute to specify the fully qualified name of the Java class that implements the handler. Use the <init-params> element to specify any initialization parameters of the handler.
  3. <handler-chains>   <handler-chain name="myChain">      <handler class-name="myHandlers.handlerOne" />      <handler class-name="myHandlers.handlerTwo" />      <handler class-name="myHandlers.handlerThree" />   </handler-chain></handler-chains>
  4. Use the <operation> child element of the <operations> element (which itself is a child of the <web-service> element) to specify that the handler chain is an operation of the Web Service.

Note: The handler chain executes on its own, without a backend component. In this case, use only the handler-chain attribute of the <operation> element and explicitly do not specify the component or method attributes, as shown in the following excerpt:

<web-service>   <operations>      <operation name="chainService"                 handler-chain="myChain" />   </operations></web-service>

Here is the web-service.xml for the example.

web-service.xml

<web-services xmlns_xsd="http://www.w3.org/2001/XMLSchema">   <handler-chains>      <handler-chain name="AuthenticationHandlerChain">         <handler class-name="AuthenticationHandler" />      </handler-chain>   </handler-chains>   <web-service name="BalanceEnquiryService"                uri="/BalanceEnquiryService"                targetNamespace="http://www.bea.com">      <components>         <java-class name="jccomp0"                     class-name="BalanceEnquiryService">         </java-class>      </components>      <operations>         <operation method="getBalance"                    handler-chain="AuthenticationHandlerChain"                    component="jccomp0">            <params>               <param name="param0" class-name="java.lang.String"                                    style="in" type="xsd:string" />               <return-param name="result"                             class-name="java.lang.Float"                                           type="xsd:float" />            </params>         </operation>      </operations>   </web-service></web-services>

Here, the handler-chain is named AuthenticationHandlerChain, which contains a handler AuthenticationHandler. Note that the operation includes the attribute handler-chain=”AuthenticationHandlerChain”.

Here is the ant script build.xml to package the EAR. Note that Web.xml and application.xml should be available before building the EAR.

build.xml

<project name="webservices-handler" default="all" basedir="."><!--  set global properties for this build  --><property environment="env" /><property name="compiler" value="modern" /><property name="debug" value="yes" /><property name="deprecation" value="yes" /><property name="build.compiler" value="${compiler}" /><property name="source" value="." /><property name="build" value="${source}/build" /><property name="war_file" value="BalanceEnquiry.war" /><property name="ear_file" value="BalanceEnquiry.ear" /><property name="namespace" value="http://www.bea.com" /><property name="client.classes.dir" value="{source}/clientclasses" /><target name="all" depends="clean, war, ear" /><target name="clean"><delete dir="${build}" /><delete file="${source}/${ear_file}" /><delete file="${war_file}" /></target><target name="war"><delete dir="${build}" /><mkdir dir="${build}" /><mkdir dir="${build}/WEB-INF" /><mkdir dir="${build}/WEB-INF/classes" /><javac srcdir="${source}" includes="AuthenticationHandler.java,                          BalanceEnquiryService.java"                          destdir="${build}/WEB-INF/classes" /><copy todir="${build}/WEB-INF"><fileset dir="${source}"><include name="web-services.xml" /><include name="web.xml" /></fileset></copy><jar jarfile="${war_file}" basedir="${build}" /></target><target name="ear" depends="war"><delete dir="${build}" /><mkdir dir="${build}" /><mkdir dir="${build}/META-INF" /><copy todir="${build}"><fileset dir="${source}"><include name="${war_file}" /></fileset></copy><copy todir="${build}/META-INF"><fileset dir="${source}"><include name="application.xml" /></fileset></copy><jar jarfile="${source}/${ear_file}" basedir="${build}" /></target></project>

The ant script creates a WAR and then packages the EAR.

Deploy the generated EAR in Weblogic server. The following is the client to invoke the BalanceEnquiryService.

Client.java

import org.apache.axis.client.Call;import org.apache.axis.client.ServiceFactory;import org.apache.axis.client.Service;import org.apache.axis.MessageContext;import org.apache.axis.attachments.Attachments;import org.apache.axis.message.SOAPEnvelope;import javax.xml.namespace.QName;import javax.xml.soap.SOAPBody;import javax.xml.soap.SOAPBodyElement;import javax.xml.soap.SOAPConnection;import javax.xml.soap.SOAPConnectionFactory;import javax.xml.soap.SOAPElement;import javax.xml.soap.SOAPHeader;import javax.xml.soap.SOAPHeaderElement;import javax.xml.soap.SOAPMessage;import javax.xml.soap.SOAPPart;import javax.xml.namespace.QName;import javax.xml.rpc.ParameterMode;import javax.xml.soap.AttachmentPart;import javax.xml.soap.MessageFactory;import javax.xml.soap.Name;import javax.xml.rpc.ParameterMode;import java.net.URL;import java.util.Iterator;public class Client{   private static String TARGET_NAMESPACE = "http://www.bea.com";   private static QName xsdFloat = new                  QName("http://www.w3.org/2001/XMLSchema", "float");   public static org.apache.axis.message.SOAPEnvelope env = null;   public static SOAPMessage message = null;   public static void main(String[] argv) throws Exception   {      Client client = new Client();      env = client.constructSOAPEnvelope();      client.constructHeader(env);      client.constructBody(env);      System.setProperty( "javax.xml.rpc.ServiceFactory",                          "org.apache.axis.client.ServiceFactory" );      String url =     "http://localhost:7001/BalanceEnquiry/BalanceEnquiryService";     ServiceFactory factory =         (org.apache.axis.client.ServiceFactory)ServiceFactory.              newInstance();      QName serviceName   = new QName(TARGET_NAMESPACE,         "BalanceEnquiryService");      Service service =          (org.apache.axis.client.Service)factory.          createService(serviceName);      Call call = (org.apache.axis.client.Call)service.createCall();      call.setTargetEndpointAddress(url);      SOAPEnvelope result = call.invoke(env);      System.out.println(result);   }   public SOAPEnvelope constructSOAPEnvelope() throws Exception   {      org.apache.axis.message.SOAPEnvelope env = new         org.apache.axis.message.SOAPEnvelope();      return env;   }   public void constructHeader(SOAPEnvelope envelope) throws Exception   {      SOAPHeader header = envelope.getHeader();      Name headerElementName =         envelope.createName("AccountDetails","",                             "http://schemas.xmlsoap.org/soap/                                     envelope/");      SOAPHeaderElement headerElement =         header.addHeaderElement(headerElementName);      headerElement.setMustUnderstand(false);      headerElement.addNamespaceDeclaration("soap",         "http://schemas.xmlsoap.org/soap/envelope/");      SOAPElement accNo = headerElement.addChildElement("accountNo");      accNo.addTextNode("12345");      SOAPElement pinNo = headerElement.addChildElement("pin");      pinNo.addTextNode("6789");   }   public void constructBody(SOAPEnvelope envelope) throws Exception   {      SOAPBody body = envelope.getBody();      Name bodyRootElementName =         envelope.createName("getBalance","",                             "http://schemas.xmlsoap.org/soap/                                     encoding/");      SOAPBodyElement bodyRootElement =         body.addBodyElement(bodyRootElementName);      SOAPElement bodyElement =         bodyRootElement.addChildElement("param0");      bodyElement.addAttribute(envelope.createName("xsi:type"),                                                   "xsd:string");      bodyElement.addTextNode("12");   }}

If there is a client side handler available, make the following changes:

  1. Import the following two classes.
    import javax.xml.rpc.handler.HandlerInfo;import javax.xml.rpc.handler.HandlerRegistry;
  2. Include the following lines before call.invoke().
    QName portName = new QName( "http://bea.com/",                             "HelloWorldServicePort");HandlerRegistry registry = service.getHandlerRegistry();List handlerList = new ArrayList();handlerList.add( new HandlerInfo( ClientHandler.class, null,                 null ) );registry.setHandlerChain( portName, handlerList );

Conclusion

We hope this article helps you understand more about the the Handler chain mechanism and its usage in Web Services.

About the Authors

Nandhini Arumugam holds a Masters degree in Computer Applications from PSG College of Technology, Coimbatore. She has been working as a software engineer in the Telecom and Mobile Solutions Lab, Hewlett-Packard, Bangalore for more than one year. The domain of her experience includes Web Services and J2EE-related technologies. She can be reached at [email protected].

Jeyarani Venkatasamy holds a Masters degree in Computer Applications from Sri Sarada College for Women, Tirunelveli. She has been working as a senior sofware engineer in the Telecom and Mobile Solutions Lab, Hewlett-Packard, Bangalore for more than one year. The domain of her experience includes Web Services, Java, and J2EE-related technologies. She can be reached at [email protected].

Latest Posts

Related Stories