Chapter 9.  Create handlers for SOAP web services.

The handler framework allows interception of a message at various points in its transmission. Handlers are simple Java bean classes that implement a handler contract and can be associated with web service endpoints and web service clients. With outgoing messages, handlers are invoked before a message is sent to the wire. With incoming messages, handlers are invoked before the receiving application receives the message. The same handler implementation is used for both incoming and outgoing messages.

JAX-WS provides two levels of handlers:

Logical handlers

Example below shows a logical handler skeleton. The main method is the handleMessage method. The close method is to clean up any resources that handler invocation might have consumed. The handleFault method is invoked if an error condition occurs, for example, if a response message contains a fault.


import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;

public class HelloMessengerLogicalHandler implements LogicalHandler<LogicalMessageContext> {

    public void close(MessageContext ctx) {
    }

    public boolean handleFault(LogicalMessageContext ctx) {
        return false;
    }

    public boolean handleMessage(LogicalMessageContext ctx) {
        return false;
    }
}

				

The handleMessage method (and handleFault) return a boolean. Returning true from the handleMessage method tells the JAX-WS run time that processing should move to the next handler in the chain. Returning false tells the JAX-WS run time that processing of the handler chain should end.

The parameter to handleMessage is a LogicalMessageContext. It is an extension of java.util.Map and contains <key, value> pairs of context properties. There are properties for items such as WSDL element names and attachment information (if any). Since handlers are invoked for both incoming and outgoing messages, a useful property is the MESSAGE_OUTBOUND_PROPERTY defined on the MessageContext interface, which gives you the direction of the message:

Boolean outbound = ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
				

Another useful property is the message itself:

LogicalMessage message = ctx.getMessage();
				

You can get the payload as XML data by using the javax.xml.transform.Source:

Source payload = message.getPayload();
				

Alternatively, you can get the payload as JAXB objects:

Object jaxbPayload = message.getPayload(jaxbContext);
				

In either case, you get the payload. In the case of SOAP, the payload is the contents of the SOAP:Body, either in XML form (javax.xml.transform.Source) or in JAXB form (java.lang.Object).

Protocol handlers

The primary reason for writing a SOAP handler is to manipulate SOAP headers. Example below shows an example of a SOAP handler skeleton:


import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class HelloMessengerProtocolHandler implements SOAPHandler<SOAPMessageContext> {

    public Set<QName> getHeaders() {
        return null;
    }

    public void close(MessageContext ctx) {
    }

    public boolean handleFault(SOAPMessageContext ctx) {
        return false;
    }

    public boolean handleMessage(SOAPMessageContext ctx) {
        return false;
    }
}

				

The SOAP handler skeleton has the familiar methods of close, handleFault, and handleMessage. The parameter to handleFault and handleMessage, however, is now SOAPMessageContext instead of LogicalMessageContext. In addition, the getHeaders method is a new method to implement.

The getHeaders method returns the set of the header names that the handler understands. The JAX-WS run time calls this method to determine whether the handler can process SOAP headers that must be understood (as indicated by the SOAP mustUnderstand attribute). It does not call this method to filter handler invocation. All handlers in a chain are called for all messages.

The SOAPMessageContext class adds a few properties to the context map that are SOAP-specific, such as roles. The getMessage method returns a SOAPMessage, which is an SAAJ class. By using the SOAPMessage, you can programmatically examine or modify SOAP message headers.

9.1.  Configure SOAP and logical handlers on the server side.

9.1.1.  Use @HandlerChain annotation.

With JAX-WS, you can declaratively specify a web service handler chain by using the @javax.jws.HandlerChain annotation. Example below shows how to apply a handler chain to the HelloMessenger web service:

import javax.jws.HandlerChain;
import javax.jws.WebService;

@WebService
@HandlerChain(file = "handler-chain.xml")
public class HelloMessenger {
    public String sayHello(String name) {
        return String.format("Hello %s", name);
    }
}
						

The HelloMessenger web service contains a class-level @HandlerChain annotation, which specifies that the handler configuration should be loaded from the handler-chain.xml file that is available in the class path. Example below shows the handler-chain.xml file in its entirety:


<?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>HelloMessengerProtocolHandler</javaee:handler-class>
        </javaee:handler>
    </javaee:handler-chain>
</javaee:handler-chains>

						

The XML file tells the JAX-WS run time to use the HelloMessengerProtocolHandler to handle incoming and outgoing messages.

9.1.2.  Use deployment descriptors.

The webservices.xml deployment descriptor file was introduced by the first version of [JSR 109] to define the set of web services that are to be deployed in a container. However, with JAX-WS, the use of webservices.xml is optional since the annotations can be used to specify most of the information specified in this deployment descriptor. When you are deploying JAX-WS web services, the only reason you should use webservices.xml is to override or augment the annotation member attributes — or when you do not want to use annotations because you don't want to modify the Java source code.

One situation, where you might want to override or augment annotations, occurs when you want to deploy a handler with a web service endpoint that has not been annotated for a handler. Alternatively, the endpoint may have been annotated for a handler, but you wish to use a different handler. One way to deal with this is to edit the source code to update or add the @HandlerChain annotation. But perhaps you do not have access or authority to modify the source code. Or perhaps you think it is a bad idea to have multiple versions of the same source code just so you can support using the endpoint with different handlers. In such a situation, you can deploy the endpoint with a custom webservices.xml to specify the handler chain.

Example below shows the Hello web service endpoint with an @HandlerChain annotation that specifies the myhandler.xml handler configuration file:

@HandlerChain(file="myhandler.xml")
@WebService
public class Hello {

    @Resource
    WebServiceContext context;

    public String sayHello(String s) {
        String appendString = (String) context.getMessageContext().get(HelloHandler.APPEND_STRING);
        return "Hello: " + s + "[appended by handler: " + appendString + "]";
    }
}
						

The myhandler.xml file is shown in below. Notice that it specifies the handler class HelloHandler.


<handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
  <handler-chain>
    <handler>
      <handler-class>HelloHandler</handler-class>
    </handler>
  </handler-chain>
</handler-chains>

						

Now, suppose you would prefer to deploy this endpoint with the handler class ImprovedHelloHandler. You could do that by bundling the webservices.xml file shown below in the WEB-INF directory of the WAR module (or, if this were an EJB endpoint, in the META-INF directory).

The webservices.xml overrides the @HandlerChain annotation:


<webservices xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://www.ibm.com/webservices/xsd/javaee_web_services_1_2.xsd">
    <webservice-description>
        <webservice-description-name>HelloService</webservice-description-name>
        <port-component>
            <port-component-name>Hello</port-component-name>
            <wsdl-service xmlns:ns1="http://samples/">ns1:HelloService</wsdl-service>
            <wsdl-port xmlns:ns1="http://samples/">ns1:HelloPort</wsdl-port>
            <service-impl-bean>
                <servlet-link>Hello</servlet-link>
            </service-impl-bean>
            <handler-chains>
                <handler-chain>
                    <handler>
                        <handler-name>myhandler</handler-name>
                        <handler-class>ImprovedHelloHandler</handler-class>
                    </handler>
                </handler-chain>
            </handler-chains>
        </port-component>
    </webservice-description>
</webservices>

						

In this webservices.xml deployment descriptor, notice the handler-chains element in the bottom half of the listing. Here, the handler-class element specifies ImprovedHelloHandler.

When using the webservice.xml, it is important to understand how the port-component names match up with the annotations. In other words, port-component-name relates to @WebService.name; wsdl-service relates to @WebService.serviceName; wsdl-port relates to @WebService.portName; and service-endpoint-interface relates to @WebService.endpointInterface.

Professional hosting         'Oracle Certified Expert Web Services Developer 6' Quiz     Free SCDJWS 5.0 Guide