by Young Yang

Get a handle on the JAX-WS API’s handler framework

news
Feb 2, 200739 mins

Learn how to use the handler framework in your Web services projects

The handler framework in the Java API for XML Web Services (JAX-WS) allows applications to address cross-cutting and/or system-level concerns by opening the service and client runtimes for applications to plug in modular components. Reusability of these components across the services portfolio is one obvious benefit that this framework brings to service delivery. This mechanism also allows the separation of the most fundamental concerns of application software in Web services development, effectively abstracting the system service into handlers and leaving the clients and services to focus on business logic.

In this article, I describe how the JAX-WS handler framework works and demonstrate, with examples, how to work with some of its most useful features.

There are currently two JAX-WS specification versions: 2.0 and 2.1. Bundled with the Java Platform, Standard Edition 6.0 (Java SE) release is version 2.0. Since the 2.1 version does not modify the handler framework in any major way and its reference implementation is still in beta, I base our expositions in this article on version 2.0. In addition, instead of using Java SE 6.0, we develop our examples with JDK 1.5.0_09 and use the JAX-WS 2.0 reference implementation. The initial Java SE 6.0 release lacks the Web service tools (probably a bug) and, therefore, has no advantage for our purposes.

Notes about the example application

I refer to the example application frequently in this article; therefore, let me introduce it briefly up front. In the download provided in Resources, there are four simple Eclipse projects:

  • jaxws-handler: Includes most of the handlers to be used. In addition, the Ant tasks in the build file help to package these handlers into a jar file (handlers.jar) and distribute this jar for other projects’ reference.
  • jaxws-handler-service: The Web service provider, simulating a credit card authorization service.
  • jaxws-handler-client1: A standard client for the credit card authorization service, developed with the help of the wsimport tool.
  • jaxws-handler-client2: A Dispatch-based client for the credit card authorization service.

Here is the WSDL (Web Services Description Language) for the Web service (CardService) in jaxws-handler-service:

<definitions xmlns:tns="http://cardservice.handler.jaxws.company.com/service" 
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
   xmlns="http://schemas.xmlsoap.org/wsdl/" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns:ns1="http://cardservice.handler.jaxws.company.com/creditcard" 
   xmlns:ns2="http://cardservice.handler.jaxws.company.com/exception" 
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
   targetNamespace="http://cardservice.handler.jaxws.company.com/service" 
   name="CardService">
    <types>
        <schema targetNamespace="http://cardservice.handler.jaxws.company.com/service"
            xmlns="http://www.w3.org/2001/XMLSchema">
            <import schemaLocation="CreditCardAuthorization.xsd" namespace="
                http://cardservice.handler.jaxws.company.com/creditcard" id="ns1"/>
            <import schemaLocation="FaultInfo.xsd" namespace="http://cardservice.
                handler.jaxws.company.com/exception" id="ns2"/>
        </schema>
    </types>
    <message name="AuthorizePayment">
        <part name="parameters" element="ns1:AuthorizationRequest"/>
    </message>
    <message name="AuthorizeStatus">
        <part name="result" element="ns1:AuthorizationStatus"/>
    </message>
    <message name="CardServiceException">
        <part name="FaultInfo" element="ns2:FaultInfo"/>
    </message>        
    <portType name="CardServicePortType">
        <operation name="authorizePayment">
            <input message="tns:AuthorizePayment"/>
            <output message="tns:AuthorizeStatus"/>
            <fault name="CardServiceException" message="tns:CardServiceException"/>
        </operation>
    </portType>
    <binding name="CardServiceBinding" type="tns:CardServicePortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="authorizePayment">
            <soap:operation soapAction="tns:authorizePayment"/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
            <fault name="CardServiceException">
                <soap:fault name="CardServiceException" use="literal"/>
            </fault>
        </operation>
    </binding>
    <service name="CardService">
        <port name="CardServicePort" binding="tns:CardServiceBinding">
            <soap:address location="http://localhost:8484/creditcard/CardService"/>
        </port>
    </service>
</definitions>

It refers to two XML schema files. One is CreditCardAuthorization.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
   xmlns:tns="http://cardservice.handler.jaxws.company.com/creditcard" 
   xmlns:xs="http://www.w3.org/2001/XMLSchema" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   targetNamespace="http://cardservice.handler.jaxws.company.com/creditcard">
            <complexType name="CreditCard">
                <sequence>
                    <element name="cardNumber" type="string"/>
                    <element name="ccvNumber" type="string"/>
                    <element name="billingAddress" nillable="true" type="tns:Address"/>                    
                </sequence>
            </complexType>
            <complexType name="Address">
                <sequence>
                    <element name="addressLine1" nillable="true" type="string"/>
                    <element name="addressLine2" nillable="true" type="string"/>
                    <element name="city" nillable="true" type="string"/>
                    <element name="state" nillable="true" type="string"/>
                    <element name="zip" nillable="true" type="string"/>
                </sequence>
            </complexType>
            <complexType name="CardUser">
                <sequence>
                    <element name="firstName" nillable="true" type="string"/>
                    <element name="lastName" nillable="true" type="string"/>
                </sequence>
            </complexType>
            <element name="AuthorizationRequest">
                <complexType>
                <sequence>
                    <element name="CreditCard" nillable="true" type="tns:CreditCard"/>
                    <element name="CardUser" nillable="true" type="tns:CardUser"/>
                </sequence>
                </complexType>
            </element>            
            <element name="AuthorizationStatus">
                <complexType>
                <sequence>
                    <element name="authorizationToken" nillable="true" type="string"/>
                    <element name="authorized" type="boolean"/>
                    <element name="errorCode" type="int"/>
                </sequence>
                </complexType>
            </element>    
   </schema>

The other is FaultInfo.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
   xmlns:tns="http://cardservice.handler.jaxws.company.com/exception" 
   xmlns:xs="http://www.w3.org/2001/XMLSchema" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   targetNamespace="http://cardservice.handler.jaxws.company.com/exception">
    <element name="FaultInfo">
       <complexType>
          <sequence>
             <element name="faultMessage" nillable="true" type="string"/>
          </sequence>
          </complexType>
    </element>
</schema>

To build the Web service using those two contracts, we customize the schemas for JAXB (Java Architecture for XML Binding) binding with custom-schema-cc.xml and custom-schema-ex.xml, respectively, and customize the WSDL with custom-wsdl.xml. We use those files as binding input for the wsimport tool to generate portable Web service artifacts. Those customizations map the target namespaces of schemas to various Java class packages. The actual implementation of the Web service is provided in the class com.company.jaxws.handler.cardservice.CreditCardServiceImpl, whose implementation of the Web service operation authorizePayment() provides authorization results based on random numbers.

The final Web service is deployed in Apache Tomcat 5.5.4. To run the Web service properly in Tomcat, we must copy the jars in the lib directory of the reference implementation to the ${CATALINA_HOME}/shared/lib directory.

Let’s also talk a bit about the handlers here. This is a list of all the handlers from the jaxws-handler project:

  • ClientAuthenticationSOAPHandler: Sets a userid and password pair to the SOAP header, and should be deployed at the client side only.
  • ServiceAuthenticationSOAPHandler: Retrieves the userid and password pair from the SOAPHeader, and should be deployed at the service side only.
  • ClientPerformanceMonitorLogicalHandler: Measures the time that a message exchange takes from entering this handler (inbound) to returning (outbound). The time includes those exchanges taken by a round-trip network, service execution, and possibly other handlers at the client and service sides. It should be deployed at the client side.
  • ServicePerformanceMonitorLogicalHandler: Measures the time that a message exchange takes from entering this handler (inbound) to returning (outbound). The time includes those exchanges taken by service execution and possibly other handlers at the service sides. It should be deployed at the service side.
  • EnvelopeLoggingLogicalHandler: Logs the whole SOAP message and may be deployed at either the service side or client side.
  • HTTPHeaderLoggingLogicalHandler: Logs the HTTP request headers and is deployed at the service side, when the binding protocol is HTTP.
  • JAXPPayloadLoggingLogicalHandler: Demonstrates how to retrieve the message payload using javax.xml.transform.Source. It can be deployed at either the service or client side.

Another handler, JAXPPayloadLoggingLogicalHandler, demonstrates how to retrieve the message payload using a JAXB context. This handler is located in jaxws-handler-service because it depends on CardService‘s domain objects.

ClientAuthenticationSOAPHandler and ServiceAuthenticationSOAPHandler should be deployed in pairs at the client and service sides, respectively.

A final note about the organization of those projects: By separating the example application into four units, I try to follow what would actually happen in reality while developing Web service applications with JAX-WS, particularly when the handler framework is needed. I hope this makes it easier for readers to understand the different deployment approaches on the server and client sides, and the different client models. In addition, I also hope this helps to emphasize that handlers should be generic and do not have to be attached to a specific Web service.

With this preparation, I now turn to the main topic of this article and begin with the types of handlers that JAX-WS 2.0 supports.

Logical and protocol handlers

JAX-WS specifies two types of handlers:

  • Protocol handlers are specific to a protocol and may access or change the protocol-specific aspects of a message
  • Logical handlers are protocol-agnostic and act only on the payload of a message

The only protocol handler interface defined in JAX-WS is javax.xml.ws.handler.soap.SOAPHandler.

Figure 1 depicts the class hierarchy directly associated with handlers:

Figure 1. Handler Interfaces in JAX-WS 2.0. Click on thumbnail to view full-sized image.

MessageContext is an important concept in JAX-WS, and from the above diagram, we can see it is particularly so when developing handlers.

Message contexts

MessageContext is the information carrier among client, client runtime, and the handlers associated with a message exchange on the client side, and service, service runtime, and service-side handlers on the server side. It contains information about the message and the context of the message exchange, such as information related to the binding protocol.

The following diagram depicts the message context class hierarchy in JAX-WS:

Figure 2. Message context interfaces in JAX-WS 2.0. Click on thumbnail to view full-sized image.

A MessageContext may be injected into the service implementation class to provide or consume message-exchange context information. LogicalMessageContext and SOAPMessageContext are inputs to LogicalHandler and SOAPHandler, respectively.

JAX-WS defines the following standard properties in the MessageContext interface:

  • MESSAGE_OUTBOUND_PROPERTY (Boolean): The message direction—true for outbound messages, false for inbound. Handlers may use this property to determine if the processing is on an outbound or inbound message.
  • INBOUND_MESSAGE_ATTACHMENTS (java.util.Map): Attachments to an inbound message. These can be used to acquire the attachments in inbound message.
  • OUTBOUND_MESSAGE_ATTACHMENTS (java.util.Map): Attachments to an outbound message. A proxy can use these to send attachments not described through WSDL MIME binding.

In the attachment-related maps, the key is the MIME Content-ID, and the value is a DataHandler for the attachment data.

The following properties are specific to the HTTP protocol and are available only in HTTP-based bindings:

  • HTTP_REQUEST_METHOD (java.lang.String): The name of the HTTP method with which a request is made; for example, GET, POST, or PUT.
  • HTTP_REQUEST_HEADERS (java.util.Map): The HTTP headers for the request message.
  • QUERY_STRING (String): The HTTP query string for the request message.
  • PATH_INFO (String): Extra path information associated with the request URL. This information follows the base url path, but precedes the query string.
  • HTTP_RESPONSE_CODE (java.lang.Integer): The HTTP response status code for the last invocation.
  • HTTP_RESPONSE_HEADERS (java.util.Map): The HTTP response headers.

In JAX-WS 2.0, developers can use QUERY_STRING and PATH_INFO properties to support REST-style (Representational State Transfer) Web services.

The properties below are specific to endpoints with HTTP-based binding, running inside a servlet container:

  • SERVLET_CONTEXT (javax.servlet.ServletContext): The ServletContext object of the Web application that contains the Web endpoint.
  • SERVLET_REQUEST (javax.servlet.http.HttpServletRequest ): The HTTPServletRequest object associated with the request currently being served.
  • SERVLET_RESPONSE (javax.servlet.http.HttpServletResponse): The HTTPServletResponse object associated with the request currently being served.

The following properties are optional and might be presented only if the binding has information about WSDL metadata:

  • WSDL_DESCRIPTION (java.net.URI): A resolvable URI that may be used to obtain access to the WSDL for the endpoint.
  • WSDL_SERVICE (javax.xml.namespace.Qname): The name of the service being invoked.
  • WSDL_PORT (javax.xml.namespace.Qname): The name of the port to which the current message is addressed.
  • WSDL_INTERFACE (javax.xml.namespace.Qname): The name of the port type to which the current message belongs.
  • WSDL_OPERATION (javax.xml.namespace.Qname): The name of the WSDL operation with which the current message is associated.

The namespace of those QNames is the target namespace of the WSDL.

Developers may also define application-specific properties in the message context to share states between client/service and handlers.

MessageContext.Scope is an enum defining property scope. Properties scoped as APPLICATION are visible to handlers, client applications, or service endpoint implementations; those scoped as HANDLER are only visible to handlers.

To check the scope of a property in MessageContext, we can use MessageContext.Scope getScope(String name). And the following method can set or change a property’s scope: void setScope(String name, MessgeContext.Scope scope).

Developing handlers in JAX-WS

As is clear from the handler class hierarchy diagram, all handlers in JAX-WS must implement the following three methods.

  • handleMessage( ): Called for inbound and outbound messages.
  • handleFault( ): Called instead of handleMessage( ) when the message contains a protocol fault.
  • close( ): Called after completion of message processing by all handlers after the completion of a message exchange pattern (MEP).

JAX-WS specifies in clear terms the semantics of the handleMessage() and handleFault() methods both for the return values and various exceptional scenarios. The close() method may be used to clean up any resources created during message processing.

In addition, a SOAPHandler also must implement the getHeaders() method, which should return header blocks’ QNames, with each QName representing the qualified name of the outermost element of one header block.

For applications or JAX-WS runtimes to manage a handler’s lifecycle, the handler can annotate a method with the @PostConstruct annotation so it will be called after the handler is created in the runtime. The @PreDestroy annotation allows the method to be called before the handler is destroyed. All those annotated methods must return void and take no arguments.

Let’s now look at the handlers in our example application.

First, the abstract class BaseHanlder<T extends MessagetContext> provides simple implementations for close() and handleFault(). All handlers in the example application extend from this base class. In addition, this base handler also comes with two methods init() and destroy(), annotated for managing the handler’s lifecycle. Those methods print notifications to notify readers when those lifecycle methods are called. For example, at the service side, the method annotated with @PostConstruct is called when the handler is deployed, while un-deploying the handler will cause the method annotated with @PreDestroy to be called.

Here is the BaseHandler<T extends MessageContext> class:

public class BaseHandler<T extends MessageContext> {
   protected String HandlerName = null;

   protected PrintStream out = System.out;

   @PostConstruct
   public void init() {
      out.println("------------------------------------");
      out.println("In Handler " + HandlerName + ":init()");
      out.println("Exiting Handler " + HandlerName + ":init()");
      out.println("------------------------------------");
   }

   @PreDestroy
   public void destroy() {
      out.println("------------------------------------");
      out.println("In Handler " + HandlerName + ":destroy()");
      out.println("Exiting Handler " + HandlerName + ":destroy()");
      out.println("------------------------------------");
   }


   public boolean handleFault(T mc) {
      out.println("------------------------------------");
      out.println("In Handler " + HandlerName + ":handleFault()");
      out.println("Exiting Handler " + HandlerName + ":handleFault()");
      out.println("------------------------------------");
      return true;
   }
   
   public void close(MessageContext mc) {
      out.println("------------------------------------");
      out.println("In Handler " + HandlerName + ":close()");
      out.println("Exiting Handler " + HandlerName + ":close()");
      out.println("------------------------------------");
   }

   public void setHandlerName(String handlerName) {
      HandlerName = handlerName;
   }
   
}

Our next handler is the EnvelopeLoggingSOAPHandler, a SOAP handler that logs the message in exchange to system output stream. This snippet captures the major part of this class:

public class EnvelopeLoggingSOAPHandler 
   extends BaseHandler<SOAPMessageContext> 
   implements SOAPHandler<SOAPMessageContext> {
    
      private static PrintStream out = System.out;
      private static final String HANDLER_NAME = "EnvelopeLoggingSOAPHandler";
     
      public EnvelopeLoggingSOAPHandler(){
         super();
         super.setHandlerName(HANDLER_NAME);        
      }
    
      public boolean handleMessage(SOAPMessageContext smc) {
         out.println("------------------------------------");
         out.println("In SOAPHandler " + HandlerName + ":handleMessage()");
         logToSystemOut(smc);
         out.println("Exiting SOAPHandler " + HandlerName + ":handleMessage()");
         out.println("------------------------------------");    
        return true;
      }

      private void logToSystemOut(SOAPMessageContext smc) {
         Boolean outboundProperty = (Boolean)
           smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        
         if (outboundProperty.booleanValue()) {
            out.println("ndirection = outbound ");
        } else {
           out.println("ndirection = inbound ");
        }
        
        SOAPMessage message = smc.getMessage();
        try {
           out.println("");   
           message.writeTo(out);
           out.println("");   
        } catch (Exception e) {
           out.println("Exception in SOAP Handler #1: " + e);
        }
   }

......
}

We identify the message direction by checking the standard property MESSAGE_OUTBOUND_PROPERTY on the SOAPMessageContext passed into handleMessge(), and retrieve the message in exchange by calling getMessage() on this same object.

Message direction is relative to client application on the client side, and to service endpoint implementation at the server side. This important information decides what is available in the message context (such as the properties HTTP_REQUEST_HEADERS or HTTP_RESPONSE_HEADERS), and whether the message contained in the message context is for request or for response. Handlers may need to implement different logic for messages of different directions.

A SOAPMessageContext‘s getMessage() method provides the whole SOAP envelope (both SOAP header and body parts). The same method on a LogicalMessageContext object returns only the payload of the message in exchange. In the SOAP binding case, this means LogicalMessageContext‘s getMessage() only returns the SOAP body part.

Another handler, JAXPPayloadLoggingLogicalHandler, also logs the message in exchange, but it is a logical handler, and therefore, can only print the SOAP body part. The following snippet captures the main part of this class:

public class JAXPPayloadLoggingLogicalHandler 
extends BaseHandler<LogicalMessageContext> 
implements LogicalHandler<LogicalMessageContext> {
   private static PrintStream out = System.out;

   private static final String HANDLER_NAME = "JAXPPayloadLoggingLogicalHandler";

   public JAXPPayloadLoggingLogicalHandler() {
      super();
      super.setHandlerName(HANDLER_NAME);
   }

   public boolean handleMessage(LogicalMessageContext smc) {
      out.println("------------------------------------");
      out.println("In LogicalHandler " + HandlerName + ":handleMessage()");

      logToSystemOut(smc);

      out.println("Exiting LogicalHandler " + HandlerName
            + ":handleMessage()");
      out.println("------------------------------------");

      return true;
   }

   private void logToSystemOut(LogicalMessageContext smc) {
      Boolean outboundProperty = (Boolean) smc
            .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

      if (outboundProperty.booleanValue()) {
         out.println("ndirection = outbound ");
      } else {
         out.println("ndirection = inbound ");
      }

      LogicalMessage message = smc.getMessage();

      try {
         Source src = message.getPayload();
         out.println(HandlerUtil.getXMLFromSource(src));
      } catch (Exception e) {
         out.println("Exception in " + HandlerName + ":" + e);
      }
   }
}

The getMessage() method on LogicalMessageContext returns a LogicalMessage object. With LogicalMessage, we can work with both javax.xml.transform.Source or a JAXBContext.

With the JAXPPayloadLoggingLogicalHandler, we retrieve the payload as a Source object; and with another handler, JAXBPayloadLoggingLogicalHandler, we input a JAXBContext object into the LogicalMessage‘s getMessage() method and marshal the payload into domain objects in Java.

We can also modify the payload with a LogicalMessage object in logical handlers using a Source object, or with the help of a JAXB context.

Working with message contexts

The examples in the previous section demonstrate how to work with MessageContext in handlers. This class can also be used directly in the service implementation class and indirectly in the client application.

A service endpoint implementation may obtain the MessageContext object through a WebServiceContext. The service runtime will inject the WebServiceContext on any field marked with @Resource. To get a MessageContext instance, we can call the WebServiceContext‘s getMessageContext() method. For an example, here is a snippet from CreditCardServiceImpl.java:

public class CreditCardServiceImpl implements CardServicePortType {
   private static Random rdm;
   ......
    
   @Resource
   WebServiceContext wsContext;

   public AuthorizationStatus authorizePayment(AuthorizationRequest request) throws CardServiceException {
      try {
         MessageContext msgContext = wsContext.getMessageContext();         
         Boolean authnStatus = (Boolean)msgContext.get("authnStatus");

         ......
         if (authnStatus != null) cardInfo.append("Authentication Status as Reported by
            ServiceAuthentricationSOAPHandler is:" + authnStatus.toString() + "n");
         cardInfo.append("********************************************** n");
         System.out.println(cardInfo.toString());

         ......
      } catch (Exception e) {
         ......
      }
   }

   ......
}

The code tries to retrieve a property called authnStatus from the MessageContext and print it to the system output stream. We set the property in ServiceAuthenticationSOAPHandler.java as follows:

public class ServiceAuthenticationSOAPHandler extends
      BaseHandler<SOAPMessageContext> implements
      SOAPHandler<SOAPMessageContext> {
   ......

   public boolean handleMessage(SOAPMessageContext smc) {
      boolean exit = false;
      out.println("------------------------------------");
      out.println("In SOAPHandler " + HandlerName + ":handleMessage()");

      boolean direction = ((Boolean) smc
            .get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY))
            .booleanValue();
      if (direction) {
         out.println("direction = outbound");
      } else {
         out.println("direction = inbound");

         // ......
         try {
            // get SOAP envelope from SOAP message
            SOAPEnvelope se = smc.getMessage().getSOAPPart().getEnvelope();

            // get the headers from envelope
            SOAPHeader sh = se.getHeader();
            if (sh == null) {
               out.println("--- No headers found in the input SOAP request");
               exit = false;
            } else {
               // call method to process header
               exit = processSOAPHeader(sh);
            }
         } catch (Exception ex) {
            ex.printStackTrace();
            exit = false;
         }
         // ......
      }

      out.println("Exiting SOAPHandler " + HandlerName + ":handleMessage()");
      out.println("------------------------------------");
      
 if (exit){
         smc.put(AuthenticationHandlerConstants.AUTHN_STAUTS, true);
      }else{
         smc.put(AuthenticationHandlerConstants.AUTHN_STAUTS, true);
      }      
      smc.setScope(AuthenticationHandlerConstants.AUTHN_STAUTS, MessageContext.Scope.APPLICATION);      
      return exit;
   }

   ......
}

Note the property is scoped to APPLICATION, because only APPLICATION-scoped properties are visible to service implementation classes. Here, the AuthenticationHandlerConstants.AUTHN_STAUTS denotes the string authnStatus. We use the string directly in the service implementation class to prevent this class from depending on the interface AuthenticationHandlerConstants, which is part of the jaxws-handler project.

With a MessageContext instance available, the service implementation class may also send information to the handlers deployed in the service runtime. The diagram below depicts the interaction:

Figure 3. MessageContext, handler and service implementation class. Click on thumbnail to view full-sized image.

A client application can configure metadata through the request context and response context obtained from the BindingProvider interface (an instance of port proxy or Dispatch). The request and response contexts are of type java.util.Map<String,Object> and are returned with the getRequestContext() and getResponseContext() methods.

The contents of the request context are used to initialize the message context prior to invoking any handlers for the outbound message. Each property within the request context is copied to the message context with a scope of HANDLER. The message context’s contents are used to initialize the response context after invoking any handlers for an inbound message. The response context is first emptied, and then each property in the message context that has a scope of APPLICATION is copied to the response context. This figure captures the interaction graphically:

Figure 4. MessageContext, RequestContext, ResponseContext, handler and Web service client. Click on thumbnail to view full-sized image.

The following code snippet from CardServiceClient.java in jaxws-handler-client1 shows how to access and update a request context with a port proxy:

public class CardServiceClient {
   public static void main(String[] args) {
      try {          
            QName serviceName = new QName("http://cardservice.handler.jaxws.company.com/service", "CardService");
           URL wsdlLoc = new URL("http://localhost:8484/creditcard/CardService?wsdl");
           CardService service = new CardService(wsdlLoc, serviceName);
         CardServicePortType port = service.getCardServicePort();
         
         Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext();
         requestContext.put("authn_userid", "yyang");
         requestContext.put("authn_password", "mypassword");
……
      } catch (Exception e) {
         e.printStackTrace();
      }
   }   
}

The client casts the port proxy to BindingProvider, obtains the request context from this interface, and sets a pair of userid and password into the context. CardServiceClient.java in jaxws-handler-client2 uses similar code to work with a Dispatch instance and achieves the same purpose.

The ClientAuthenticationSOAPHandler below demonstrates how a handler can pick up this userid and password pair set in the request context by a client:

public class ClientAuthenticationSOAPHandler …… {
   ……

   public boolean handleMessage(SOAPMessageContext smc) {
      out.println("------------------------------------");
      out.println("In SOAPHandler " + HandlerName + ":handleMessage()");

      boolean direction = ((Boolean) smc
            .get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY))
            .booleanValue();

      String USERID = (String) smc
            .get(AuthenticationHandlerConstants.REQUEST_USERID);
      String PASSWORD = (String) smc
            .get(AuthenticationHandlerConstants.REQUEST_PASSWORD);

      if (direction) {
         out.println("direction = outbound");

         // ......
         try {

            /*
             * add a header with the authentication info into the SOAP
             * message:
             * 
             * <env:Header> <nsAuthen:authnHeader
             * xmlns:nsAuthn="http://cardservice.handler.jaxws.company.com/authn">
             * <nsAuthn:id>yyang</nsAuthn:id> <nsAuthn:password>mypassword</nsAuthn:password>
             * </nsAuthn:authnHeader> </env:Header>
             */

            // get SOAP envelope from SOAP message
            SOAPEnvelope envelope = smc.getMessage().getSOAPPart()
                  .getEnvelope();

            // create instance of SOAP factory
            SOAPFactory soapFactory = SOAPFactory.newInstance();

            // create SOAP elements specifying prefix and URI
            SOAPElement headerElm = soapFactory.createElement(
                  AuthenticationHandlerConstants.AUTHN_LNAME,
                  AuthenticationHandlerConstants.AUTHN_PREFIX,
                  AuthenticationHandlerConstants.AUTHN_URI);
            SOAPElement idElm = soapFactory.createElement("id",
                  AuthenticationHandlerConstants.AUTHN_PREFIX,
                  AuthenticationHandlerConstants.AUTHN_URI);

            // attach value to id element
            idElm.addTextNode(USERID);
            SOAPElement passwdElm = soapFactory.createElement("password",
                  AuthenticationHandlerConstants.AUTHN_PREFIX,
                  AuthenticationHandlerConstants.AUTHN_URI);

            // attach value to password element
            passwdElm.addTextNode(PASSWORD);

            // add child elements to the root element
            headerElm.addChildElement(idElm);
            headerElm.addChildElement(passwdElm);

            // create SOAPHeader instance for SOAP envelope
            SOAPHeader sh = envelope.addHeader();

            // add SOAP element for header to SOAP header object
            sh.addChildElement(headerElm);
         } catch (Exception ex) {
            ex.printStackTrace();
         }
      } else {
         out.println("direction = inbound");
      }
      // ......

      out.println("Exiting SOAPHandler " + HandlerName + ":handleMessage()");
      out.println("------------------------------------");

      return true;
   }

……
}

The two lines of interest are:

String USERID = (String) smc
            .get(AuthenticationHandlerConstants.REQUEST_USERID);
      String PASSWORD = (String) smc
         .get(AuthenticationHandlerConstants.REQUEST_PASSWORD);

In a typical use-case, the handler inserts the information as SOAP headers to convey the same information to service-side (SOAP) handlers or the service-endpoint implementation. This is exactly what the above code snippet does. In our example application, at the service side, a ServiceAuthenticationSOAPHandler retrieves the userid and password pair from the SOAP header, and uses it to authenticate the request.

Authenticating Web service requests using user name and password as we do with ClientAuthenticationSOAPHandler and ServiceAuthenticationSOAPHandler is obviously for the purpose of understanding the concepts only. Real applications need to address a whole set of other concerns and should follow the protocol defined by the Web Services Security standard.

This simple authentication implementation shows how states and parameters are passed into handlers through MessageContext from the context of an application client request. The ClientPerformanceMonitorLogicalHandler.java and CardServiceClient.java from jaxws-handler-client1 demonstrates how information transfers in the opposite direction, where an APPLICATION-scoped property set by a handler is passed to the client with MessageContext as the carrier. The property in consideration is ELAPSED_TIME, which ClientPerformanceMonitorLogicalHandler puts into the LogicalMessgeContext. The CardServiceClient picks it up from the response context and prints it to the system output stream.

Client-side programmatic configuration of handler chains

Developing and programming with handlers is only the first step. To make them serve a Web service application, we must configure and deploy them into the client or service runtime. In typical cases, multiple handlers will be in operation, and they are organized into chains. Handler configuration and deployment use handler chains as units.

There are two ways in JAX-WS to configure handler chains: programmatically or through metadata. Programmatic configuration of handler chains is only applicable at the client side.

To understand how a client may configure a handler chain for a particular MEP, let’s look at the following diagram, which captures the relevant actors in JAX-WS client runtime:

Figure 5. Client-side class and interfaces for handler chain configuration. Click on thumbnail to view full-sized image.

A Web service client application needs to implement the HandlerResolver interface and register a HandlerResolver instance with a Service instance. When the client creates binding providers to interact with the Web service, the runtime knows the PortInfo under concern, calls the getHandlerChain() method of the currently registered handler resolver to create the handler chains (based on the PortInfo), and further uses those chains to configure the binding provider.

CardServiceHandlerResolver.java in jaxws-handler-client2 provides a HandlerResolver example:

public class CardServiceHandlerResolver implements HandlerResolver {

   public List<Handler> getHandlerChain(PortInfo portInfo) {
      List<Handler> handlerChain = new ArrayList<Handler>();

      ClientAuthenticationSOAPHandler authn = new ClientAuthenticationSOAPHandler();
      EnvelopeLoggingSOAPHandler logging = new EnvelopeLoggingSOAPHandler();
      ClientPerformanceMonitorLogicalHandler perf = new ClientPerformanceMonitorLogicalHandler();
      JAXPPayloadLoggingLogicalHandler payload = new JAXPPayloadLoggingLogicalHandler();

      QName serviceName = portInfo.getServiceName();
      QName portName = portInfo.getPortName();
      String bindingID = portInfo.getBindingID();

      if (serviceName.getNamespaceURI().equals(
            "http://cardservice.handler.jaxws.company.com/service")
            && serviceName.getLocalPart().equalsIgnoreCase("CardService")) {
         handlerChain.add(authn);
         handlerChain.add(logging);
      }

      if (portName.getNamespaceURI().equals(
            "http://cardservice.handler.jaxws.company.com/service")
            && portName.getLocalPart().equalsIgnoreCase("CardServicePort")) {
         handlerChain.add(perf);
      }

      if (bindingID.equals("http://schemas.xmlsoap.org/wsdl/soap/http")) {
         handlerChain.add(payload);
      }

      if (bindingID
            .equals("http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/")) {
         handlerChain.add(payload);
      }

      return handlerChain;
   }
}

The getHandlerChain() method constructs a handler chain based on the passed PortInfo object.

The following code snippet from CreditCardServiceClient.java in jaxws-handler-client2 shows how a Dispatch-based client may use this HandlerResolver:

public class CreditCardServiceClient {
   ......
   
   public void authorizePayment(String url, Service.Mode mode)
         throws Exception {
      String xmlString = null;
      try {
    
         Service service = Service.create(wsdlLoc, serviceName);
         service.setHandlerResolver(ccResolver);

         if (mode.equals(Service.Mode.PAYLOAD)) {
         
            Dispatch<Source> disp = service.createDispatch(portName,
                  Source.class, mode);

            Map<String, Object> requestCtx = ((BindingProvider) disp)
                  .getRequestContext();
            requestCtx.put(AuthenticationHandlerConstants.REQUEST_USERID,
                  "yyang");
            requestCtx.put(AuthenticationHandlerConstants.REQUEST_PASSWORD,
                  "mypassword");

            xmlString = CreditCardUtil.readStringFromFile(requestXMLFile);
            Source xmlSource = (Source) new StreamSource(new StringReader(
                  xmlString));

            Source response = disp.invoke(xmlSource);

            System.out.println(CreditCardUtil.sourceToXMLString(response));

         }
         
         if (mode.equals(Service.Mode.MESSAGE)) {
         ......
         }
      } catch (Exception e) {
         ......
      }
   }
   ......
}

With this handler resolver approach, the handler chain is automatically resolved for a BindingProvider (port proxy or Dispatch instance). The Binding interface in the JAX-WS API provides a second approach, configuring handler chains at a more fine-grained level.

Binding is an abstraction of a JAX-WS protocol binding. As described above, the handler chain initially configured on a binding provider instance (port proxy or Dispatch) is a snapshot of the applicable handlers configured on the service instance at the time of creation. A Binding instance provides methods to manipulate the initially configured handler chains for the owning binding provider. The following code snippet from the same source as above shows how to work with the Binding interface to configure handler chains:

public class CreditCardServiceClient {
   ......
   
   public void authorizePayment(String url, Service.Mode mode)
         throws Exception {
      String xmlString = null;
      try {
         Service service = Service.create(wsdlLoc, serviceName);
         service.setHandlerResolver(ccResolver);

         if (mode.equals(Service.Mode.PAYLOAD)) {
         ......
         }
         
         if (mode.equals(Service.Mode.MESSAGE)) {
            Dispatch<SOAPMessage> disp = service.createDispatch(portName,
                  SOAPMessage.class, mode);

            Map<String, Object> requestCtx = ((BindingProvider) disp)
                  .getRequestContext();
            requestCtx.put(AuthenticationHandlerConstants.REQUEST_USERID,
                  "yyang");
            requestCtx.put(AuthenticationHandlerConstants.REQUEST_PASSWORD,
                  "mypassword");
            Binding cardServiceBinding = ((BindingProvider) disp).getBinding();            
            ClientAuthenticationSOAPHandler authnHandler = new ClientAuthenticationSOAPHandler();            
            List<Handler> handlerChain = new ArrayList<Handler>();            
            handlerChain.add(authnHandler);            
            cardServiceBinding.setHandlerChain(handlerChain);            
            xmlString = CreditCardUtil
                  .readStringFromFile(requestSOAPXMLFile);

            MessageFactory factory = MessageFactory.newInstance();
            SOAPMessage message = factory.createMessage();
            message.getSOAPPart().setContent(
                  (Source) new StreamSource(new StringReader(xmlString)));
            message.saveChanges();

            SOAPMessage response = disp.invoke(message);

            SOAPPart sp = response.getSOAPPart();
            Source resp = sp.getContent();
            System.out.println(CreditCardUtil.sourceToXMLString(resp));
         }
      } catch (Exception e) {
         ......
      }
   }
   ......
}

Handler chains set through the Binding interface take precedence over others set with other approaches.

With programmatic configuration, we can configure the client runtime with the same set of handler chains for all message exchanges. Since a PortInfo instance carries information about the qualified names of the service, the port, and the protocol binding, we can also configure, through a handler resolver, different handler chains at the client runtime for different services, ports, and protocol bindings. Further, the Binding interface allows the application of a specific set of handler chains to a particular message exchange.

Service-side handler-chain configuration through metadata

I now describe the actual mechanisms for configuring handler chains at the service side, in terms of service development approaches.

In code-first service development starting from a service-endpoint implementation or a service endpoint interface (SEI), handler chains are configured with the @HandlerChain annotation defined in Java Specification Request 181, Web Services Metadata for the Java Platform. For example, we may use the following for CreditCardServiceImpl.java and, from here, develop the Web service with a handler chain configured:

@WebService(……)
 @HandlerChain(file="handler-chain.xml")
 public class CreditCardServiceImpl {
 ...
 }

The handler-chain.xml referenced in the annotation is the handler chains’ descriptor, and this is an example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <javaee:handler-chain>
   <javaee:handler>
      <javaee:handler-class>com.company.jaxws.handler.cardservice.common.ServiceAuthenticationSOAPHandler
      </javaee:handler-class>
      </javaee:handler>
      <javaee:handler>
         <javaee:handler-class>com.company.jaxws.handler.cardservice.common.EnvelopeLoggingSOAPHandler
         </javaee:handler-class>
      </javaee:handler>
      <javaee:handler>
         <javaee:handler-class>com.company.jaxws.handler.cardservice.common.JAXBPayloadLoggingLogicalHandler
         </javaee:handler-class>
      </javaee:handler>
      <javaee:handler>
         <javaee:handler-class>com.company.jaxws.handler.cardservice.common.HTTPHeaderLoggingLogicalHandler
         </javaee:handler-class>
      </javaee:handler>                
      <javaee:handler>
         <javaee:handler-class>com.company.jaxws.handler.cardservice.common.ServicePerformanceMonitorLogicalHandler
         </javaee:handler-class>
      </javaee:handler>
   </javaee:handler-chain>
</javaee:handler-chains>

The schema for the handler chains descriptors is defined in javaee_Web_services_1_2.xsd (see Resources), which is the schema file accompanying JSR 109, Web Services for Java EE, version 1.2.

JAX-WS 2.0 does not define a standard approach for configuring hander chains for contract-first service development starting from WSDL. However, with the reference implementation, we can use external binding declaration to customize the WSDL and configure the wsimport tool for generating annotated service class and service endpoint interfaces together with corresponding handler chains descriptors. For an example, consider the following external binding declaration file, service-config-single.xml, which merely embeds the above descriptor into an jaxws:bindings element:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="CreditCardService.wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">    
    <bindings node="wsdl:definitions">
        <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee">
            <javaee:handler-chain>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.ServiceAuthenticationSOAPHandler
                    </javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.EnvelopeLoggingSOAPHandler
                    </javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.JAXBPayloadLoggingLogicalHandler
                    </javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.HTTPHeaderLoggingLogicalHandler
                    </javaee:handler-class>
                </javaee:handler>                
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.ServicePerformanceMonitorLogicalHandler
                   </javaee:handler-class>
                </javaee:handler>
            </javaee:handler-chain>
        </javaee:handler-chains>
    </bindings>
</bindings>

With this file as a binding input, the wsimport Ant task produces two handler chains descriptors, CardService_handler.xml and CardServicePortType_handler.xml, with the same content as the embedded handler chains descriptor. It also annotates the generated service class CardService.java and the service endpoint interface CardServicePortType.java with @HandlerChain, using these two descriptors as configuration files.

Notes:

  1. Omitting the node attribute for jaxws:bindings in the above binding declaration file will have the same effect.
  2. Since javaee:handler-chains is not recognized as a valid WSDL extension in JAX-WS 2.0 and 2.1, we can not use embedded binding declarations for customizing WSDL for handler chains as we can with other WSDL customizations (see Chapter 8 of the JAX-WS 2.0 or 2.1 specification).

If we also annotate the service implementation class, which may or may not implement the generated and @HandlerChain-annotated CardServicePortType interface, the handler chains declared in the implementation class will take precedence.

As a summary, handler chains applicable to the service runtime in JAX-WS are configured completely at service development time. Unlike the client side, there is no mechanism for manipulating handler chains at runtime on the service side.

Configuring service-side handler chains based on services, ports, and protocol bindings

As discussed before, we can programmatically configure, at the client side, different handler chains for different message exchanges based on services, ports, and protocol bindings. How do we achieve the same purpose on the service side with metadata?

WSDL customizations in JAX-WS usually apply to different nodes in WSDL and thus customize certain features (e.g., asynchrony) for those applied services, ports, and operations. Unfortunately, customizing WSDL by nodes does not work for handler chains. To allow selection of handler chains based on service, port, and protocol binding in operation for a message exchange at the service runtime, we must specify constraints for handler chains in the handler chains’ descriptor (or the one embedded in the binding declaration for WSDL customization).

There are three such elements we can specify as subelements for javaee:handler-chain, javaee:service-name-pattern, javaee:port-name-pattern, or javaee:protocol-binding, respectively restricting the chain defined by the parent element to apply for services and ports with certain name patterns, or for a specific binding protocol. If none of these elements are specified within the handler-chain element, the handlers specified in the chain are applied to everything.

Here is an example of a binding declaration file customizing WSDL for handler chains, with constraints specified for different chains (service-config-multiple-chains.xml):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="CreditCardService.wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">    
    <bindings node="wsdl:definitions">
        <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee">
            <javaee:handler-chain>
            <javaee:service-name-pattern xmlns:ns1="http://cardservice.handler.jaxws.
                        company.com/service">ns1:CardService</javaee:service-name-pattern>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.
                        EnvelopeLoggingSOAPHandler</javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.
                        JAXBPayloadLoggingLogicalHandler</javaee:handler-class>
                </javaee:handler>
            </javaee:handler-chain>
            <javaee:handler-chain>
               <javaee:port-name-pattern xmlns:ns2="http://cardservice.handler.jaxws.
                       company.com/service">*</javaee:port-name-pattern>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.
                        ServiceAuthenticationSOAPHandler</javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.
                        ServicePerformanceMonitorLogicalHandler</javaee:handler-class>
                </javaee:handler>
            </javaee:handler-chain>
            <javaee:handler-chain>
               <javaee:protocol-bindings>##SOAP11_HTTP</javaee:protocol-bindings> 
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.
                        HTTPHeaderLoggingLogicalHandler</javaee:handler-class>
                </javaee:handler>
            </javaee:handler-chain>
        </javaee:handler-chains>
    </bindings>
</bindings>

Both javaee:service-name-pattern and javaee:port-name-pattern need qualified names, and the namespaces must be declared in the containing context. Also, we can use the wild card * in the names.

The javaee:protocol-bindings element requires a space-delimited list of protocol binding URIs or, for the standard binding types, their aliases. The following table lists the URIs of the standard binding types in JAX-WS and their corresponding aliases:

URIAlias
http://schemas.xmlsoap.org/wsdl/soap/http##SOAP11_HTTP
http://schemas.xmlsoap.org/wsdl/soap/http?mtom=true##SOAP11_HTTP_MTOM
http://www.w3.org/2003/05/soap/bindings/HTTP/##SOAP12_HTTP
http://www.w3.org/2003/05/soap/bindings/HTTP/?mtom=true##SOAP12_HTTP_MTOM
http://www.w3.org/2004/08/wsdl/http##XML_HTTP

Metadata and client-side handler chain configuration

JAX-WS 2.0 does not mandate support for the @HandlerChain annotation on generated service classes or service endpoint interfaces for client-side consumption. However, when an implementation does support this feature, the JAX-WS 2.0 specification states that we can regard it as shorthand for defining and installing a handler resolver. Furthermore, the specification requires the handler resolver to return handler chains consistent with the contents of the handler chains’ descriptor referenced by the @HandlerChain annotation.

The reference implementation of JAX-WS 2.0 in Java SE 6 supports the @HandlerChain annotation on generated service class and service endpoint interfaces. While it is not as powerful or flexible as configuring handler chains programmatically, the @HandlerChain annotation allows service clients to more easily work with handler chains.

I do want to point out that, if the service client is a Web client or an application client that runs in a Java EE container, the @HandlerChain annotation can be specified on Web service references. This sets the handlers on the injected service. For example:

public class WebClient extends HttpServlet {
        @javax.jws.HandlerChain(file="myhandler.xml")
        @WebServiceRef HelloService service;

       public void doGet(HttpServletRequest req, 
            HttpServletResponse resp) 
            throws javax.servlet.ServletException {
       ...
       }
   }

Also note that when using the reference implementation, constraints on handler chains descriptors do not seem to work on the client side. All handler chains are picked up by all message exchanges no matter what constraints are in the descriptor.

Configuring handler chains in the Web service deployment descriptor

With the reference implementation and its support for Tomcat, GlassFish, and Sun’s application server, we can also configure handlers in the sun-jaxws.xml deployment descriptor. Handler chains specified here will override handlers configured with WSDL customizations or annotated classes.

To specify handler chains for a service endpoint in sun-jaxws.xml, just embed the handler chains’ descriptor under the corresponding endpoint element. Here is an example:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints
    xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
    version="2.0">
    <endpoint
    name="CardService"
        implementation="com.company.jaxws.handler.cardservice.CreditCardServiceImpl" 
    wsdl="WEB-INF/wsdl/CreditCardService.wsdl"
        service="{http://cardservice.handler.jaxws.company.com/service}CardService"
        port="{http://cardservice.handler.jaxws.company.com/service}CardServicePort"
        url-pattern="/CardService">
        <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee">
            <javaee:handler-chain>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.ServiceAuthenticationSOAPHandler
                    </javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.EnvelopeLoggingSOAPHandler
                    </javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.JAXBPayloadLoggingLogicalHandler
                    </javaee:handler-class>
                </javaee:handler>
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.HTTPHeaderLoggingLogicalHandler
                    </javaee:handler-class>
                </javaee:handler>                
                <javaee:handler>
                    <javaee:handler-class>com.company.jaxws.handler.cardservice.common.ServicePerformanceMonitorLogicalHandler
                    </javaee:handler-class>
                </javaee:handler>
            </javaee:handler-chain>
        </javaee:handler-chains>
   </endpoint>
</endpoints>

To actually test it out, we must rename the original sun-jaxws.xml file to sun-jaxws.xml. Other Java EE application servers or servlet containers may have different Web service deployment descriptors; therefore, this approach for configuring handler chains at the service side only applies to the Sun implementation and its support for JAX-WS.

Execution sequence of handlers

In a handler-chain descriptor, there may be multiple chains, and in each of them, logical handlers may coexist with protocol handlers. The question is, in what sequence are handlers of different kinds, and in different chains, executed? Since the execution order of handlers may affect logics implemented in them, and their collaboration, developers must understand the answer to this question.

Here is the behavior defined in the JAX-WS specification and as implemented in the reference implementation:

  • Whether there are multiple handler chains or just one, all logical handlers are executed before protocol handlers on an outbound message, and all protocol handlers are executed before logical handlers on an inbound message.
  • The reordering process in the first rule honors the relative positions of protocol handlers or logical handlers configured inside any handler chain on an outbound message; for an inbound message, the order is reversed.
  • If multiple handler chains are configured, handlers (of the same kind) from chains in the front of the descriptor apply before handlers from those in the back on an outbound message, and handlers (of the same kind) from chains in the back of the descriptor apply before handlers from chains in the front on an inbound message.

Readers may try different handler-chain configuration scenarios with jaxws-handler-service and jaxws-handler-client1 and see those rules in operation.

The following figure shows how handlers are reordered during a request and response scenario. Two handler chains are configured at both the client and service sides, with one chain consisting of two logical handlers and one SOAP handler, and the other consisting of one logical handler and two SOAP handlers.

Figure 6. Execution sequence of handlers in handler chains. Click on thumbnail to view full-sized image.

Suggested improvements

There are a couple of things about the handler framework I feel JAX-WS could improve.

The @HandlerChain annotation is an extremely convenient approach for configuring handler chains in both client and service runtimes, but it falls short in flexibility. I would like to see a future version of the JAX-WS specification make it applicable to methods as well as to types.

Along the same line, binding handler chains with Web services through external binding declarations greatly simplifies service development in Java. For this reason, I hope a future version of the specification and its reference implementation will allow, in such declarations, handler chains to apply to all the relevant nodes in WSDL such as wsdl:definitions, wsdl:portType, and wsdl:operation, following, for example, what it is possible with WSDL customization for asynchrony.

In JAX-WS, handlers are associated with services, and the specification leaves the handler deployment architecture for developers to decide, depending on the implementation. SOAP engines implementing JAX-WS tend to bundle a service with its handlers as a single deployment unit. This may make sense for handlers processing service-specific logic. However, for more generic handlers, this practice makes reuse across services inconvenient and unnecessarily complicates the packaging and deployment of Web services reusing such handlers.

Following a different approach, Apache Axis2 deploys modules, which serve more or less the same purpose as handlers in JAX-WS, directly into the SOAP engine (instead of attaching them to services), and then associates services and their operations with those modules through a configuration file. I have always believed this should be the preferred deployment architecture for components serving cross-cutting concerns and hope a future version of JAX-WS will follow a similar approach when standardizing handler deployment.

Acknowledgement: The example Web service used in this article is adapted from the one used in “Document Handling Using JAX-WS Dispatch and Provider APIs” by Deep Singh; however, we have changed it completely from a technical point of view.

Young Yang has a Ph.D in mathematics and works in software, consulting, and corporate IT as a tech lead, architect, development, and project manager. He is a PMP and Six Sigma Green Belt, and was certified by IBM on e-Business Design, DB2 Administration, Business Intelligence, XML and Related Technologies, and WebSphere Application Server; by BEA on WebLogic Application Server; and by Sun as an Enterprise Architect.