Implement a SOAP logging mechanism for testing and debugging a Web service application using Java EE Web Service APIs.

[Note]

Handlers are message interceptors that can be easily plugged in to the JAX-WS runtime to do additional processing of the inbound and outbound messages. JAX-WS defines two types of handlers, logical handlers and protocol 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 cannot change any protocol-specific parts (like headers) of a message. Logical handlers act only on the payload of the message.

SOAP handlers are generally used to process SOAP-specific information, such as SOAP headers. For example, a SOAP Handler can process security headers in a message and pass the request to the endpoint if the message has the required credentials. Logical handlers are commonly used if the processing does not need access to SOAP headers, for validation of the payload, and with Representational State Transfer ("REST") style web services. In addition, logical handlers can use Java API for XML Binding (JAXB) for processing the payload. If you have the JAXBContext object, it's simple to alter something in a message with a logical handler. You can get the JAXB objects and call Java methods on them rather than dealing with Source objects or SOAPMessage objects using the SOAP with Attachments API for Java (SAAJ) in a SOAP handler.

Handlers are invoked with a message context, which provides methods that the handler uses to access and modify inbound or outbound messages. The message context also has properties that the handler can process. These properties can be used to communicate additional information or metadata that is not specified in the message. The additional information can be exchanged between a handler and service implementation or between a handler and a web service client.

SOAP handlers extend javax.xml.ws.handler.soap.SOAPHandler. The JAX-WS specification defines the SOAPHandler class for SOAP binding. When a SOAP handler is invoked, a SOAPMessageContext object is specified in the request. The SOAPMessageContext object provides methods to access a SOAPMessage (the class for SOAP messages). Specifically, the method getMessage() in SOAPMesageContext retrieves the SOAPMessage. After getting a SOAPMessage, you can use SAAJ to manipulate it.

Logical handlers extend javax.xml.ws.handler.LogicalHandler. They provide access to the message context and the message payload. If you use SOAP over HTTP for the message exchange, the content of the SOAP body forms the payload. If you use XML over HTTP, the XML content of the primary part of the message forms the payload. When a logical handler is invoked, a LogicalMessageContext object is specified in the request. The method getMessage() in LogicalMessageContext returns a LogicalMessage. The LogicalMessage represents a protocol-neutral XML message and contains methods that provide access to the payload of the message.

The following figure illustrates the relationship between the message contexts, the objects they can be used to retrieve, and the parts of a message those objects cover:

Message Contexts

Logical handlers can coexist with SOAP handlers in a handler chain. During runtime, the handler chain is reordered so that for an outbound message the logical handlers execute BEFORE the SOAP handlers. For an inbound message, the SOAP handlers execute BEFORE the logical handlers.

Handler Chain

Writing a SOAP Message Handler

Let's look at a simple SOAP handler. SOAPLoggingHandler is a SOAP handler that logs calls to a web service. Here's a code snippet from SOAPLoggingHandler:


public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {
    ... 
   
    public boolean handleMessage(SOAPMessageContext smc) {
        logToSystemOut(smc);
        return true;
    }
       
    public boolean handleFault(SOAPMessageContext smc) {
        logToSystemOut(smc);
        return true;
    }
       
    public void close(MessageContext messageContext) {
    }
      
    ...
}

					

Like all SOAP handlers, SOAPLoggingHandler implements the SOAPHandler interface. Notice that SOAPLoggingHandler also implements three methods: handleMessage(), handleFault(), and close(). The SOAPHandler interface is a subinterface of the Handler interface. The handleMessage(), handleFault(), and close() methods are the three methods defined in the Handler interface, and should be implemented in all basic handlers. Here's how the methods are used:

The logging is done in the handler's logToSystemOut() method that both the handleMessage() and handleFault() methods call:

private void logToSystemOut(SOAPMessageContext smc) {
    Boolean outboundProperty = (Boolean)smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        
    if (outboundProperty.booleanValue()) {
        out.println("\nOutbound message:");
    } else {
        out.println("\nInbound message:");
    }
        
    SOAPMessage message = smc.getMessage();
    try {
        message.writeTo(out);
        out.println("");   
    } catch (Exception e) {
        out.println("Exception in handler: " + e);
    }
}
					

Notice that the logToSystemOut() method accesses a SOAPMessageContext property, MESSAGE_OUTBOUND_PROPERTY, and uses a SOAPMessageContext method, getMessage(). The value of MESSAGE_OUTBOUND_PROPERTY indicates the message direction. A value of true means that the message is outbound. A value of false indicates an inbound message. Based on the property value, logToSystemOut() prints either "Outbound message" or "Inbound message". It then uses getMessage() to get the SOAP message, and prints it.

Writing a Logical Message Handler

The LogicalLoggingHandler file is a logical message handler. Like the SOAPLoggingHandler, it logs calls to a web service. Here's a code snippet from LogicalLoggingHandler:


public class LogicalLoggingHandler implements LogicalHandler<LogicalMessageContext> {
    ...
   
    public boolean handleMessage(LogicalMessageContext context) {
        return processMessage(context);
    }
       
    public boolean handleFault(LogicalMessageContext context) {
        return processMessage(context);
    }
       
    public void close(MessageContext context) {
        // Clean up Resources
    }
}

					

Like all logical handlers, LogicalLoggingHandler implements the LogicalHandler interface. And like all handlers it implements the handleMessage(), handleFault(), and close() methods.

The logging is done in the handler's processMessage() method that both the handleMessage() and handleFault() methods call:

private boolean processMessage(LogicalMessageContext context) {
    Boolean outboundProperty = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
   
    if (outboundProperty) {
        out.println("\nOutbound message:");
    } else {
        out.println("\nInbound message:");
    }
    
    LogicalMessage lm = context.getMessage();
    Source payload = lm.getPayload();
   
    // Process Payload Source
    printSource(payload);
    // ....
          
    // If the payload is modified, 
    // do lm.setPayload(source) to be safe. Without it, 
    // behavior may vary on the kind of source returned in 
    // lm.getPayload().
    // See LogicalMessage JavaDoc for more details.
    // lm.setPayload(modifiedPayload);
    return true;
}
					

The processMessage() method accesses and manipulates the message payload using the logical message context for the logical handler. It calls the LogicalMessage method, getPayload(), to get the payload of the message. The payload is returned as a Source object.

Notice the commented out portion of processMessage(). In some cases, you might need to call the LogicalMessage method, setPayload(), after a modification is made to the payload. The source returned by getPayload() depends on the JAX-WS runtime. If the returned source is DOMSource, a modification to the encapsulated DOM tree changes the message payload in place. In that case, there is no need to subsequently call setPayload(). However, other types of returned source provide only read access to the message. In those cases, you need to call setPayload() after any modifications.

You can also pass a JAXBContext object in the call to getPayload(), to get the payload as a JAXB object. Here's an example:

LogicalMessage lm = context.getMessage();
Object jaxbObject = lm.getPayload(jaxbContext);
// Modify JAXB Object
lm.setPayload(modifiedJaxbObject,jaxbContext);
					

Note that there is no connection between the returned object and the message payload - to change the payload, you need to call setPayload().

Professional hosting     Belorussian informational portal         Free SCWCD 1.4 Study Guide     Free SCDJWS 1.4 Study Guide     SCDJWS 1.4 Quiz     Free IBM Certified Associate Developer Study Guide     IBM Test 000-287. Enterprise Application Development with IBM WebSphere Studio, V5.0 Study Guide     SCDJWS 5.0 Quiz