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

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

1 2 3 Page 3
Page 3 of 3

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.

Learn more about this topic

1 2 3 Page 3
Page 3 of 3