C H A P T E R  4

Using Message Handlers

This chapter provides an introduction to handlers and describes how the IDE supports the use of handlers with web services and clients.

A JAX-RPC message handler is a user-written class that can modify a SOAP message representing an RPC request or response. Handlers can be associated with a web service or a web service client. A handler has access to the body and header blocks of a message, but a handler does not have access to client or server application code. Handlers are normally designed to work with the header blocks and to supplement the processing done by web service or client application code.

A handler chain organizes handler classes so they execute in a designated sequence and can share data. When you add the first handler to a web service or client, the IDE automatically creates a handler chain. Any additional handlers become part of the same handler chain. There is at most one handler chain for a given web service or client.

Typical uses of handlers include:

For detailed information about how to code handlers, see Java API for XML-Based RPC (JAX-RPC) Specification 1.0: http://java.sun.com/xml/jaxrpc.

For detailed information about SOAP messages and headers, see the SOAP 1.1 specification: http://www.w3.org/TR/2000/NOTE-SOAP-20000508.



Note - This chapter should be read as an advanced topic. Most web services and clients do not require the use of handlers.




SOAP Message Handlers

The purpose of this section is to provide basic information about handlers so you can understand the handler features of the IDE, and to provide a lead-in to more detailed sources of information about handlers. If you are writing handler classes, you should also read the handler material in the JAX-RPC and SOAP specification documents.

SOAP Message Headers and Header Blocks

SOAP headers are an extension to a SOAP message. Headers are typically used to support higher level functions such as security and auditing.

In a SOAP message XML document, the Header element, if present, is the first immediate child element of the SOAP Envelope element. The immediate child elements of the Header, if present, are called header entries or header blocks.

CODE EXAMPLE 4-1 SOAP Header With One Header Block
<SOAP-ENV:Header>
	<t:Transaction xmlns:t="some-URI" 
		SOAP-ENV:mustUnderstand="1"
		SOAP:actor="www.xyz.com/actor1">
			5
	</t:Transaction>
</SOAP-ENV:Header>

CODE EXAMPLE 4-1 shows a SOAP header with one header block. The actor and mustUnderstand attributes are explained later in this chapter.

Request Handlers and Response Handlers

A handler class can process SOAP messages representing RPC requests and RPC responses. The main processing is done by two methods:

public boolean handleRequest (MessageContext context) {...}
public boolean handleResponse (MessageContext context) {...}

A handleRequest or handleResponse method gets the parts of the SOAP message from the MessageContext parameter. This includes the envelope, header, header elements, body, and body elements. See CODE EXAMPLE 4-2 for sample code.

CODE EXAMPLE 4-2 Sample Handler Code To Extract Parts of a SOAP Message
public boolean handleRequest (MessageContext context) {
 
    try {
        SOAPMessageContext smc = (SOAPMessageContext) context;
        SOAPMessage msg = smc.getMessage();
        SOAPPart sp = msg.getSOAPPart();
        SOAPEnvelope se = sp.getEnvelope();   
        SOAPHeader shd = se.getHeader();
  
        SOAPBody sb = se.getBody();                
        java.util.Iterator childElems = sb.getChildElements();
        SOAPElement child;
        StringBuffer message = new StringBuffer();
        while (childElems.hasNext()) {
            child = (SOAPElement) childElems.next();
            message.append(new Date().toString() + "--");
            formLogMessage(child, message);
 
        } ...

A handleRequest or handleResponse method can modify the headers or body of a message. The method can consume a header block (remove it from the message) or add a header block.

For example, a handleRequest or handleResponse method can write to an external log or audit file, or it can call routines to encrypt or decrypt the body of a message. A handler is not normally used to analyze or modify elements within the body of a message.

The action taken by a handleRequest or handleResponse method can depend on the content of the header or data in the body of the message, such as the name of the web service method in an RPC request or response.

There are four places in a client-service interaction where handlers can execute.

Client-side handler:

    • A request handler is invoked before the client's RPC request is sent to a service.
    • A response handler is invoked before the service's RPC response is delivered to the client.

Service-side handler:

    • A request handler is invoked before a client's RPC request is delivered to the service.
    • A response handler is invoked before the service's RPC response is sent to the client.

A single handler class can have both handleRequest and handleResponse methods.

See Sample Handler Code for an example of a handler with a handleRequest method.



Note - A JAX-RPC handler class is required to implement the interface javax.xml.rpc.handler.Handler interface.



Handler chains

A handler chain for a given web service or client is illustrated schematically in CODE EXAMPLE 4-3:

CODE EXAMPLE 4-3 Schematic Description of a Handler Chain
handler chain
	roles
		"http://acme.org/auditing"
		"http://acme.org/morphing"
	handler class "acme.MyHandler1"
		headers
			"ns1:foo ns1:bar"
			"ns2:foo ns2:bar"
		properties
			name="property1" value="xyz1"
			name="property2" value="xyz2"
			name="property3" value="xyz3"
	handler class "acme.MyHandler2"

The handler chain in the example has two handler classes, acme.MyHandler1 and acme.MyHandler2. The class acme.MyHandler1 is configured with three properties and two header blocks. Two SOAP actor roles are associated with the handler chain.

The uses of handler properties, header blocks, and SOAP actor roles are described in this chapter. The IDE manages handler chains for you. No coding is necessary.

SOAP Actor Roles

You can use SOAP actor roles to ensure that designated header blocks are processed by handlers or by the ultimate destination (web service or client) of a message.

A SOAP actor is an attribute of a SOAP header block, typically used in conjunction with the mustUnderstand attribute, as illustrated in CODE EXAMPLE 4-1.

Suppose that a header block has the following attributes:

SOAP-ENV:mustUnderstand="1" 
SOAP:actor="www.xyz.com/actor1"

If a SOAP message with this header block is processed by a handler chain that has the actor role "www.xyz.com/actor1", the header block must be consumed by one of the handlers in the handler chain or by the destination web service or client. If the header block is not consumed, the SOAP runtime throws an exception before any business method is called by the destination web service or client.

If the mustUnderstand attribute is omitted or has the value "0", the SOAP runtime does not enforce the consumption or use of the header block, regardless of any actor attribute in the header block.

The SOAP 1.1 specification explains that tagging elements with the attribute mustUnderstand="1" is intended to ensure that developers who might not understand the purpose of those elements will not erroneously ignore them. (Handlers might not be written by the same developers who design the SOAP message and its header blocks.)

Handler Properties.

A handler property can be used to configure a handler. The property and its value are passed to a handler instance at initialization time, through the HandleInfo parameter. CODE EXAMPLE 4-6 shows how a handler property might be used. The IDE enables you to add properties to a handler during development. (See Adding a Handler Property.)

Two different web services or clients might use the same handler, but with different property values, such as different log files or different encryption and decryption routines.

Getting Header Blocks at Handler Initialization

The action taken by a handler can depend on the header blocks passed in RPC requests and responses, including header blocks added to a message by other handlers executed earlier in the same handler chain. A handler might use some headers blocks and ignore others.



Note - Header blocks are the immediate child elements of the Header element (if present) in a SOAP message. See SOAP Message Headers and Header Blocks.



When you add a handler to a web service or client, you can configure the handler to process certain header blocks. The IDE enables you to provide a list of header block names during the development of a web service or client. (See Adding a SOAP Header Block To a Handler.)

The header block names that you set in the IDE are passed to the handler in a parameter of type HandlerInfo. The code fragment in CODE EXAMPLE 4-4 shows how the handler can obtain the array of headers to process. The requestHandler or responseHandler method can iterate through the QName array to get the header block names.

CODE EXAMPLE 4-4 Sample Handler Code To Initialize List of Header Blocks
public class StockServerMailHandler 
        implements javax.xml.rpc.handler.Handler 
{    
    private QName[] headers;
    
    public StockServerMailHandler () {     
    }
 
    public void init(HandlerInfo config) {
        headers = config.getHeaders();
    }
 

A runtime SOAP message in an RPC request or response might have many header blocks. They are all available to the handler through a parameter of type Message Context in the handleRequest and handleResponse methods, as illustrated in CODE EXAMPLE 4-7. However, you can code the handler so that it only processes the header blocks whose names are gathered from the Handler Info parameter during initialization.

Adding a Header Block to a SOAP Message

Handlers are not intended to be a major source of new headers, but there are some scenarios in which you must add a header block to a SOAP message. Suppose, for example, that you want a given header block to be processed by more than one handler or by a handler and the target web service or client. The problem is that if a handler consumes a header block, it is no longer part of the SOAP message. You can solve this problem by having your handler copy the header block before consuming it, add the copy to the SOAP message, and then pass the message up the chain.

The code fragment in CODE EXAMPLE 4-5 shows how to add a header block to a message. The header block must be a child element of a header element, so if the message does not already have a header element, the code adds one.

CODE EXAMPLE 4-5 Sample Handler Code To Add a Header To a SOAP Message
package stock;
 
import java.util.Hashtable;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.*;
import java.util.*;
 
public class AddHeaderHandler 
			implements javax.xml.rpc.handler.Handler {
 
    private Map props;
 
    public AddHeaderHandler () {
    }
    public void init(HandlerInfo config) {
        props = config.getHandlerConfig();
    }
    public boolean handleFault(MessageContext context) {
	return true;
    }
        public boolean handleRequest (MessageContext context) {
    
     try {
	    SOAPMessageContext smc = (SOAPMessageContext) context;
	    SOAPMessage msg = smc.getMessage();
	    SOAPPart sp = msg.getSOAPPart();
	    SOAPEnvelope se = sp.getEnvelope();
        SOAPHeader sh = se.getHeader();
        if(sh == null){
          sh = se.addHeader();
        } 
        //add header elements
                        Name headerName = se.createName("myNewHeader");
              SOAPHeaderElement she = sh.addHeaderElement(headerName);
     } 
	catch (Exception ex) {
	    //ex.printStackTrace();
	}
        	return true;
    }
    public boolean handleResponse (MessageContext context) {
	return true;
    }
 
     
    public javax.xml.namespace.QName[] getHeaders() {
	  return null;
    } 
 
    public void destroy() {
    }
}

You can also add header elements to a SOAP message through WSDL code. See the WSDL 1.1 specification: http://www.w3.org/TR/wsdl#_soap:header.


Using Handlers In the IDE

The procedures in this section assumes that you have created a web service, a client, and handler classes in the IDE.

Adding Handlers To a Web Service or Client

To add handler classes to a web service or client:

1. Right-click the web service or client node and choose Properties.

The SOAP Message Handlers properties shows the number of handlers currently associated with the web service, as illustrated in FIGURE 4-1.

 FIGURE 4-1 SOAP Message Handlers Property

Screenshot showing the SOAP Message Handlers property of a web service.

2. Click the value of the Soap Message Handlers property, and click the ellipsis (...) button.

The IDE displays the SOAP Message Handlers dialog box, as illustrated in FIGURE 4-2. In this example two handler classes are already associated with the web service.

 FIGURE 4-2 SOAP Message Handlers Dialog Box

Screenshot showing the SOAP Message Handlers Dialog Box with two handler classes.

The SOAP Message Handlers dialog box provides several features to help you manage handlers and handler chains:

  • The Add and Remove buttons enable you to add or remove handler classes from the handler chain associated with a web service or client.
  • The Move up and Move down buttons enable you to control the execution sequence of handler classes within the handler chain. Handlers at the top of the displayed list execute first.
  • The Modify properties button enables you to add a property name and value to a handler.
  • The Modify SOAP headers button enables you to specify which SOAP header blocks are processed by a handler.
  • The SOAP Actor Roles button enables you to associate one or more SOAP actor roles with the handler chain.

Each of these features is described in this chapter.

3. Click Add, select one or more desired handlers from the chooser, and click OK.

The selected handlers appear in the SOAP Message Handlers dialog box and become part of the handler chain for the web service or client.



Note - A JAX-RPC handler class is required to implement the interface javax.xml.rpc.handler.Handler interface. If you try to add a handler that does not implement this interface, the IDE will display an error message.



Adding a Handler Property

To add a property and value to a handler:

1. Right-click the web service or client node and choose Properties.

The SOAP Message Handlers properties shows the number of handlers currently associated with the web service, as illustrated in FIGURE 4-1.

2. Click the value of the Soap Message Handlers property, and click the ellipsis (...) button.

The IDE displays the SOAP Message Handlers dialog box, as illustrated in FIGURE 4-3. In this example the web service has one handler class.

 FIGURE 4-3 SOAP Message Handlers Dialog Box

Screenshot showing the SOAP Message Handlers Dialog Box with one handler class.

3. Highlight a handler and click Modify properties.

The Set Properties dialog box appears, as illustrated in FIGURE 4-4. In this example, the handler has a property named logFile, whose value is the fully-qualified name of the file to which the handler writes log information.

 FIGURE 4-4 SOAP Message Handlers Set Properties Dialog Box

Screenshot showing the SOAP Message Handlers Set Properties Dialog Box with one property.

The dialog box displays properties and values for the selected handler. You can remove a property or add a property.

4. Click Add.

The Add Properties dialog box appears, as illustrated in FIGURE 4-5. In this example, a new property is added named auditFile, whose value is the fully-qualified name of the file to which the handler writes audit information.

 FIGURE 4-5 SOAP Message Handlers Add Property Dialog Box

Screenshot showing the SOAP Message Handlers Add Property Dialog Box.

5. Enter a property name and value, and click OK.

The Set Properties dialog box appears showing the new property and value, as illustrated in FIGURE 4-6.

 FIGURE 4-6 SOAP Message Handlers Set Properties Dialog Box

Screenshot showing the SOAP Message Handlers Set Properties Dialog Box with two properties.

See CODE EXAMPLE 4-6 for sample handler code that initializes a handler with the value of the logFile property.

CODE EXAMPLE 4-6 Sample Handler Code to Use the logFile Property Value
public class StockServerLoggingHandler 
        implements javax.xml.rpc.handler.Handler 
{    
    private PrintStream pout;
    private QName[] headers;
    
    public StockServerLoggingHandler () {     
    }
    public void init(HandlerInfo config) {
        Map props = config.getHandlerConfig();
        headers = config.getHeaders();
//Get the value of the property "logFile"
        String logFileName = null;
        if(props != null)
          logFileName = (String)props.get("logFile");
        
        File logFile = new File(logFileName);
        if(!logFile.exists()){
            try{
                logFile.createNewFile();
            }
            catch(Exception e){
                e.printStackTrace();
            }    
        }
        try{            
          FileOutputStream out = new FileOutputStream(logFile, true);
          pout = new PrintStream(out);
        }catch(IOException e){
            e.printStackTrace();
        }    
    }

Adding a SOAP Header Block To a Handler

To add a SOAP header block to a handler:

1. Right-click the web service or client node and choose Properties.

The SOAP Message Handlers properties shows the number of handlers currently associated with the web service, as illustrated in FIGURE 4-1.

2. Click the value of the Soap Message Handlers property, and click the ellipsis (...) button.

The IDE displays the SOAP Message Handlers dialog box, as illustrated in FIGURE 4-3.

3. Highlight a handler and click Modify SOAP headers.

The Set SOAP Header dialog box appears, as illustrated in FIGURE 4-7.

 FIGURE 4-7 Set SOAP Header Dialog Box

Screenshot showing the Set SOAP Header Dialog Box with two header names.

The dialog box displays SOAP header blocks (SOAP Header QNames) for the selected handler. You can remove a QName or add a QName.

4. Click Add.

A dialog box appears, as illustrated in FIGURE 4-8.

 FIGURE 4-8 Set SOAP Header QName Dialog Box

Screenshot showing the Set SOAP Header Dialog Box: add QName.

5. Enter the SOAP Header QName and click OK.

The new SOAP header block name appears in the Set SOAP Header dialog box, as illustrated in FIGURE 4-7.



Note - If the header block has a namespace qualifier, it should be entered in the following syntax: {namespace-uri}localpart. The syntax is displayed in the Set SOAP Header dialog box in FIGURE 4-8. For example, {foo/myNS}header1.



These header names are available to a handler instance when it is configured at runtime. See Getting Header Blocks at Handler Initialization for information and a code sample, showing how this feature is used.



Note - This list of SOAP header block names is static information that is set up at development time for a given web service or client, and is passed to a handler when it is initialized at runtime. For information about how a handler can add a header block to a SOAP message dynamically at runtime, see Adding a Header Block to a SOAP Message.



Setting a SOAP Actor Role For a Handler Chain

The IDE enables you to associate one or more SOAP actor roles with a handler chain. See Handler chains, SOAP Actor Roles, and Enforcing the Processing of Header Blocks for an explanation of how SOAP actor roles are used.

1. Right-click the web service or client node and choose Properties.

The SOAP Message Handlers properties shows the number of handlers currently associated with the web service, as illustrated in FIGURE 4-1.

2. Click the value of the Soap Message Handlers property, and click the ellipsis (...) button.

The IDE displays the SOAP Message Handlers dialog box, as illustrated in FIGURE 4-3.

You can set SOAP actor roles for the handler chain, not for individual handlers.

3. Click SOAP Actor Roles.

The IDE displays the Configure SOAP Actor Roles dialog box, as illustrated in FIGURE 4-9.

The dialog box displays SOAP actor roles for the handler chain. You can remove an actor role or add an actor role.

 FIGURE 4-9 Configure SOAP Actor Roles Dialog Box

Screenshot showing the Configure SOAP Actor Roles Dialog Box with one actor role.

4. Click Add.

A dialog box appears, as illustrated in FIGURE 4-10.

 FIGURE 4-10 Add SOAP Actor Role Dialog Box

Screenshot showing the Configure SOAP Actor Roles Dialog Box: add actor role.

5. Enter the desired SOAP actor role and click OK.

The SOAP actor role must be expressed as a valid URI.

The new SOAP actor role appears in the Set SOAP Header dialog box, as illustrated in FIGURE 4-11.

 FIGURE 4-11 Configure SOAP Actor Roles Dialog Box

Screenshot showing the Configure SOAP Actor Roles Dialog Box with two actor roles.

Enforcing the Processing of Header Blocks

This section explains how you can ensure that a handler chain processes mandatory headers that are intended for certain SOAP actors.

A handler chain can be configured to act in the role of one or more SOAP actors, expressed as a valid URI. A handler in the handler chain can be configured to process header blocks with specified qualified names (QNames) and return the header blocks in the handler's getHeaders() method.

When a SOAP message is processed, the handler chain does the following:

1. Determines what actor roles (from the configured actors) the handler chain is to play. It also keeps track of all header blocks that each handler in the chain intends to process.

2. Examines the SOAP message header blocks (if any) and determines which ones must be processed by the handler chain, by finding all header blocks that are mandatory for its configured actors. It does this by looking at the values of the "actor" and "mustUnderstand" attributes of the header blocks.

3. Checks if each mandatory header block is in the list that the handlers intend to process, that is, it is "understood" by the handler chain. If a mandatory header block is not "understood" by the chain, a SOAPFaultException is thrown.

4. Processes the header blocks that are intended for the chain by invoking the individual handlers.

To enforce the processing of mandatory header blocks by a handler chain:

1. Add the appropriate actor roles to the handler chain.

The procedure is described in Setting a SOAP Actor Role For a Handler Chain.

2. Specify the handlers' intent to process certain headers by configuring each handler with the designated header blocks.

The procedure is described in Adding a SOAP Header Block To a Handler.

3. In the handler's code, return the designated header blocks in the getHeaders() method.

You can get the header blocks from the HandlerInfo that is passed to the init() method of the handler.

4. Process the header blocks in the handler code.


Sample Handler Code

The web service handler in CODE EXAMPLE 4-7 processes an RPC request message as follows:

1. During handler initialization, the init method gets a property that specifies the name of a file to be the target for logging messages.

2. The handleRequest method builds a log message consisting of the date and time followed by the data in the request

3. The handleRequest method writes the log message to the target log file.

CODE EXAMPLE 4-7 Sample Handler with handleRequest Method
package stock;
 
import java.util.Hashtable;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import javax.xml.namespace.*;
 
public class StockServerLoggingHandler 
        implements javax.xml.rpc.handler.Handler 
{    
    private PrintStream pout;
    private QName[] headers;
    
    public StockServerLoggingHandler () {     
    }
 
    public void init(HandlerInfo config) {
        Map props = config.getHandlerConfig();
        headers = config.getHeaders();
 
//Get the value of the property "logFile"
 
        String logFileName = null;
        if(props != null)
          logFileName = (String)props.get("logFile");
 
//if logFile property is not defined, create default directory and log file name
 
 	 if(props == null || !props.containsKey("logFile") || logFileName == null
			|| logFileName.length() == 0){
 	 logFileName = System.getProperty("user.home") + File.separator
			+ "stockhandler.log";
        }
 
// create log file if it does not already exist
 
        File logFile = new File(logFileName);
        if(!logFile.exists()){
            try{
                logFile.createNewFile();
            }
            catch(Exception e){
                e.printStackTrace();
            }    
        }
 
// associate output print stream with log file
 
        try{            
          FileOutputStream out = new FileOutputStream(logFile, true);
          pout = new PrintStream(out);
        }catch(IOException e){
            e.printStackTrace();
        }    
    }
 
    public boolean handleFault(MessageContext context) {
      return true;
    }
 
  public boolean handleRequest (MessageContext context) {
 
// get SOAP message and extract the envelope, header, body, and body elements
 
    try {
        SOAPMessageContext smc = (SOAPMessageContext) context;
        SOAPMessage msg = smc.getMessage();
        SOAPPart sp = msg.getSOAPPart();
        SOAPEnvelope se = sp.getEnvelope();   
        SOAPHeader shd = se.getHeader();
 
        SOAPBody sb = se.getBody();                
        java.util.Iterator childElems = sb.getChildElements();
        SOAPElement child;
        StringBuffer message = new StringBuffer();
 
// iterate through body elements, formatting a log message for each element
 
        while (childElems.hasNext()) {
            child = (SOAPElement) childElems.next();
            message.append(new Date().toString() + "--");
            formLogMessage(child, message);
        }
 
// extract headers
 
        if(shd != null){
            java.util.Iterator iter = 
                shd.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);
            while(iter.hasNext()){
                SOAPHeaderElement el = (SOAPHeaderElement)iter.next();
                Name n = el.getElementName();
 
                for(int i = 0; i < headers.length; i++)
				{
					QName header = headers[i];
                    String qNameSpace = header.getNamespaceURI();
                    String nNameSpace = n.getURI();
					//System.out.println("Name.getURI(): " + nNameSpace);
                    String qLocalPart = header.getLocalPart();
                    String nLocalPart = n.getLocalName();
 
// print headers
 
                    if(qNameSpace != null &&
					nNameSpace != null &&
					qNameSpace.equals(nNameSpace) &&
                       qLocalPart.equals(nLocalPart) )
					{
                        message.append(" " + n.getQualifiedName() + ":");
						Iterator iter2 = el.getChildElements();
                        while(iter2.hasNext())
						{
							Object sel = iter2.next();
                            if(sel instanceof SOAPElement)
							{
								message.append(" " + ((SOAPElement)sel).
										getElementName().getLocalName()+ ": ");
                            }
							else if(sel instanceof Text)
                            {
                                		message.append(((Text)sel).getValue());
							}                            
						}
					}
				}
            }    
        }
        pout.println(message.toString());
    } 
    catch (Exception ex) {
        ex.printStackTrace();
    } 
    return true;
  }
 
// format a log message
 
    private void formLogMessage (SOAPElement child, StringBuffer message) {
        message.append(child.getElementName().getLocalName());
        message.append(child.getValue() != null ? ":" + child.getValue() + " " 			
		: " ");
        
	try{
		java.util.Iterator childElems = child.getChildElements();
	while (childElems.hasNext()) {
		Object c = childElems.next();
		if(c instanceof SOAPElement)
		formLogMessage((SOAPElement)c, message);
        }
       }catch(Exception e){
           e.printStackTrace();
       }    
    }
    
    public boolean handleResponse (MessageContext context) {
        return true;
    }
 
    public javax.xml.namespace.QName[] getHeaders() {
 
// return headers configured on the node
 
      return headers;
    }
 
    public void destroy() {
          pout.close();
    }
}