Chapter 24. Creating SOAP Message Handler of JAX-RPC Web Service

Table of Contents

24.1. Using SAAJ
24.1.1. Creating a SOAP Message
24.1.2. Handling an SAAJ Document
24.1.3. Sending a SOAP Message through SAAJ
24.2. Creating SOAP Message Handlers
24.2.1. Creating a Message Handler
24.2.2. Designing a Message Handler and a Handler Chain
24.2.3. Creating a Handler Interface
24.2.4. Configuring Java EE Web Services DD File
24.2.5. Using a SOAP Message Handler from the Client
24.2.6. Example of File Exchanges Between Web Services and Clients

This chapter describes the SAAJ programming model and the message handler that uses JAX-RPC and SAAJ together.

24.1. Using SAAJ

Developers can produce and consume SOAP messages by using SAAJ (SOAP with Attachments API for Java). This section briefly describes the concept behind the SAAJ programming model, but it does not discuss the details of the API. For more information, refer to the SAAJ specification.

SAAJ can handle complex SOAP messages with MIME attachments as well as simple SOAP messages, like simple XML documents, without attachments. SAAJ is based on the Abstract Factory pattern, and it creates a SOAP message from the MessageFactory. A SOAPMessage includes the SOAPPart that represents the SOAP document and zero or more AttachmentPart(s) that represent MIME attachments.

[Figure 24.1] Structure of SOAP with Attachment


24.1.1. Creating a SOAP Message

In order to create an empty SOAP message, SOAPMessage object needs to be instantiated by using the MessageFactory object.

The following is an example of creating a SOAPMessage object.

MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();

24.1.2. Handling an SAAJ Document

SAAJ provides many interfaces for creating and handling SOAP messages.

A SOAP message is an XML instance that consists of elements and attributes. For convenience, the main parts of a SOAP message are typed corresponding to the SAAJ. Envelope, Header, and Body correspond to the SOAPEnvelope, SOAPHeader, and SOAPBody, respectively. The SOAPElement type corresponds to the application-specific element that is not included in the SOAP namespace.

  • SOAPPart SOAPEnvelope Type

    The SOAPPart and SOAPEnvelope are frequently used when working with SwA (SOAP with Attachment) messages. The SOAPPart is the root level of the MIME part in a SwA message, and it can be accessed by using the SOAPMessage.getSOAPPart() method.

    The SOAPEnvelope is accessed through the getEnvelope() method of the SOAPPart. The SOAPEnvelope is the root level of an XML SOAP message, and it includes methods for accessing the SOAPHeader and SOAPBody.

  • SOAPElement Type

    The SOAPElement type is used to directly represent an application-specific element that is not expressed in a namespace for SOAP 1.1 or 1.2 XML. This type can represent an XML element.

    The SOAPElement can include other SOAPElement objects as an XML element can contain other XML elements. The type is the super type of other SOAP types (SOAPEnvelope, SOAPBody, SOAPBodyElement, SOAPHeader, SOAPHeaderElement, and fault elements).

  • SOAPHeader Type

    The header element of a SOAP message can have zero or more header blocks.

    The SOAPHeader type represents the header element and the SOAPHeaderElement type represents each header block. The SOAPHeader type provides methods for adding, deleting, and checking a SOAPHeaderElement object.

  • SOAPHeaderElement Type

    The SOAPHeaderElement is abstracted from a header block to modify and check an attribute of a particular header block and its child elements. The SOAPHeaderElement type may contain one or more SOAPElement objects, as well as attributes like 'actor' and 'mustUnderstand'.

  • SOAPBody Type and SOAPBodyElement Type

    The SOAPBody type represents the body element of a SOAP message. The SOAPBodyElement extends the SOAPElement, and does not have additional methods except those that are inherited.

    The following shows an example of using of the SOAPBody and SOAPBodyElement types.

    Name echo_Name = soapFactory.createName(“echo”, “tmax”, 
                  “http://www.tmax.com/saaj/test”);
    SOAPBody body = message.getSOAPBody();
    SOAPBodyElement echo_Element = body.addBodyElement(echo_Name);

24.1.3. Sending a SOAP Message through SAAJ

Once a SOAP message has been created through SAAJ, it is ready to be sent.

SAAJ has its own simple way of sending and receiving SOAP messages, which is available as a part of the API. SAAJ enables exchanging request/response style SOAP messages through a web service.

The following is an example of instantiating a SOAPConnection object used for sending a message.

//Build a SOAPMessage from a file
MessageFactory msgFactory = MessageFactory.newInstance();
MimeHeaders mimeHeaders = new MimeHeaders();
MimeHeaders.addHeader(“Content-Type”, “text/xml; charset=UTF-8”);
FileInputStream file = new FileInputStream(“soap.xml”);
SOAPMessage requestMsg = msgFactory.createMessage(mimeHeaders,file);
file.close();

String address = “…”;

//Send the SOAP message to the BookQuote Web Service
SOAPConnectionFactory conFactory = SOAPConnectionFactory.newInstance();
URL url = new URL(address);
SOAPMessage replyMsg = connection.call(requestMsg, url);

24.2. Creating SOAP Message Handlers

A SOAP message handler processes SOAP messages that are exchanged by a JAX-RPC client and web service endpoint, and it can be used with a statically created stub, a dynamically created proxy, a DII (Dynamic Invocation Interface), a Java class endpoint, or an EJB endpoint.

The most important role of a message handler is to provide a mechanism for adding, reading, and handling a header block of a SOAP message, which is sent and received by the JAX-RPC client and web service endpoint.

The SOAP message handlers are controlled by the Java EE container. To transmit a SOAP message by using JAX-RPC client API, the SOAP message is processed through the message handler chain before the JAX-RPC runtime is sent out to the network connected with the web service. When the response is received by a JAX-RPC client, it is processed through the same message handler chain before the result returns to the client application program.

The handler is used for many different purposes. For example, a SOAP message handler can be used is to encrypt SOAP messages for more secure communication. A client can use a SOAP message handler that encrypts a SOAP message before it is sent out. Then the target web service can use a SOAP message handler to decrypt the received message, and forward it to the appropriate back-end. The response is processed in the reverse order.

Another example of using a handler is to access information from the header part of the SOAP message. The SOAP header can be used to store web service specific information, which can be manipulated by using a handler.

24.2.1. Creating a Message Handler

A SOAP message handler provides a mechanism for intercepting the SOAP message during both the request and response processing of the web service. You can create handlers from both the web service endpoint and the client applications that invoke the web service.

The following table lists the major classes and interfaces of the javax.xml.rpc.handler API.

DivisionDescription
javax.xml.rpc.handler. HandlerMain interface that contains methods for handling a SOAP request, response, and fault messages.
javax.xml.rpc.handler. HandlerChainInterface that represents a list of Handlers. The list has the java.xml.rpc.handler.Handler type element.
javax.xml.rpc.handler. HandlerRegistryInterface that supports the ability to adjust the handler configuration at the programming level.
javax.xml.rpc.handler. HandlerInfoClass that contains information about the handler defined in the webservices.xml file, e.g., handler's initialization parameters.
javax.xml.rpc.handler. MessageContextAbstracts the message context processed by the handler. The MessageContext properties allow the handler in a handler chain to share its processing state.
javax.xml.rpc.handler.soap. SOAPMessageContextSub-interface of the MessageContext interface that provides a method to access the SOAP request and response messages.
javax.xml.rpc.handler. GenericHandlerAbstract class that implements the Handler interface. A SOAP message handler may inherit the interface for implementation.
javax.xml.soap.SOAPMessageClass that contains the actual request or response SOAP message.

The following are the high-level steps for adding SOAP message handlers and handler chains to update the web service.

  1. Design the handlers and handler chains.

  2. Create a Java class to implement the javax.xml.rpc.handler.Handler interface, and implement the handler chain.

  3. Compile Java code.

  4. Configure the web services deployment descriptor file (webservices.xml).

  5. Package and deploy the web service.

A Web service client can also use SOAP message handlers that is described in the last part of this chapter.

24.2.2. Designing a Message Handler and a Handler Chain

The following information is required when designing SOAP message handlers and handler chains.

  • The number of handlers needed

  • The sequence of executions

  • Whether the web service includes message handlers or back-end components.

Handlers must be set in the webservices.xml file, and a handler chain is a group of handlers whose execution order is fixed. Each handler in a handler chain has one method for handling the SOAP request and another for handling the SOAP response.

When invoking a web service, a JEUS web service executes the handlers by using the following mechanism.

  1. The handleRequest() method of the handlers in the handler chain are all executed in the order specified in the webservices.xml file.

  2. When the handleRequest() method of the last handler in the handler chain executes, the JEUS web service invokes the back-end component that implements the web service (if the backend exists).

  3. When the back-end component has finished executing, the handlers in the handler chain are called in the reverse order, and the handleResponse() method of each handler is invoked.

  4. When the handleResponse() method of the first handler specified in the webservices.xml file executes, JEUS server returns the SOAP message response to the client application that invoked the web service.

24.2.3. Creating a Handler Interface

A SOAP message handler class can be implemented by directly implementing the javax.xml.rpc.handler.Handler interface, or inheriting the javax.xml.rpc.handler.GenericHandler that implements the javax.rpc.xml.handler.Handler interface.

The message handler class must implement the following methods of the Handler interface.

DivisionDescription
init()

The HandlerInfo object contains information about the SOAP message handler, like the initialization parameters, defined in webservices.xml. Use the HandlerInfo.getHandlerConfig() method to get the parameters. The method returns a java.util.Map object that contains name-value pairs.

Implement the init() method to process the handler's initialization parameters.

destroy()The Handler.destroy() method is called to destroy a Handler object instance. Implement this method to release any resources acquired during the handler's lifecycle.
getHeaders()The Handler.getHeaders() method is used to get the header blocks that are processed by the Handler instance.
handleRequest()

The Handler.handleRequest() method is called to intercept a SOAP message request before it is processed by the back-end component. The MessageContext object contains message content that is processed by the SOAP message handler. The SOAPMessageContext, the sub-interface of the MessageContext, can be used to get or modify the SOAP request message content.

Use the aforementioned SAAJ to process the message. By executing SOAPMessageContext.get Message(), a SOAPMessage that is a part of the SAAJ API can be obtained, and the SOAPMessage consists of the SOAPPart and attachments. The task of handling SOAP messages is the same as in SAAJ programming.

handleResponse()

The Handler.handleResponse() method is called to intercept a SOAP message response after it has been processed by the back-end component and before it is sent back to the client application that invoked the web service. The MessageContext object abstracts the message context processed by the SOAP message handler. Use the SOAPMessageContext, the sub-interface of MessageContext, to get or update the contents of the SOAP message response.

Use the aforementioned SAAJ to process the message. A SOAPMessage, that is included in the SAAJ API, can be obtained by calling SOAPMessageContext.get Message(), and the SOAPMessage consists of the SOAPPart and attachments.

The task of handling SOAP messages is the same as in SAAJ programming.

handleFault()

The Handler.handleFault() method processes SOAP fault based on the SOAP message processing model. The method is implemented to handle SOAP fault caused by the handleRequest() and handleResponse() methods and back-end errors.

The MessageContext object contains the message contents processed by the SOAP message handler, and a SOAP response message can be obtained or modified by using the SOAPMessageContext, which is a sub-interface of the MessageContext interface.

Use the aforementioned SAAJ to process the message.

24.2.4. Configuring Java EE Web Services DD File

Various SOAP message handler information and the handler processing order is defined in webservices.xml. The following is an example of configuring the message handler information.

[Example 24.1] Message Handler Settings in <port-component>

<port-component>
    <port-component-name>FileAttPort</port-component-name>
    . . .
    <handler>
        <handler-name>ServerAttachmentHandler</handler-name>
        <handler-class>
            filetransfer.ServerAttachmentHandler
        </handler-class>
        <init-param>
            <param-name>directory</param-name>
            <param-value>/temp</param-value>
        </init-param>
    </handler>
</port-component>


24.2.5. Using a SOAP Message Handler from the Client

It is also possible to use a SOAP message handler from the web services client.

Using SOAP message handler from the client is identical to a web service using a SOAP message handler. First, create a Java class that implements the javax.xml.rpc.handler.Handler interface.

The tasks after implementing a client handler is different from that of implementing a server handler. The difference is that the client handler is directly registered within the program by using the javax.xml.rpc.handler.HandlerInfo and javax.xml.rpc.handler.HandlerRegistry classes, instead of being defined in the DD. Refer to the example in the next section.

24.2.6. Example of File Exchanges Between Web Services and Clients

In this example, a web service client sends a file named “File_Send.txt” to the web service server. The web service then saves the file in a specified folder and sends a confirmation message to the client that the file has been received successfully.

A client is created in the following steps.

  1. Implement a message handler that receives a file.

  2. Implement a web service back-end that receives a file and sends a confirmation message.

  3. Configure the web service deployment descriptor, and instantiate and deploy the web service.

  4. Implement a client-side handler.

  5. Implement a web service client.

  6. Execute the client.

24.2.6.1. Implementing a Message Handler for Receiving Files

The following is the implementation of a web service message handler that is executed before the handleRequest() method sends the SOAP message to the back-end. The handleRequest() method uses the SAAJ API to handle the file attached to the SOAP message.

[Example 24.2] << ServerAttachmentHandler.java >>

package filetransfer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;

import javax.xml.rpc.handler.GenericHandler;

public final class ServerAttachmentHandler 
    extends GenericHandler
{
    private File dir;

    private final String DIR_PROP="directory";

    public void init(HandlerInfo info) {
        super.init(info);    
        Map m = info.getHandlerConfig();    

        String dirName = (String) m.get(DIR_PROP);

        if (dirName == null) {
            throw new JAXRPCException("Property named: "
                + DIR_PROP + " was not found");
        }
        dir = new File(dirName);

        if (! dir.exists()) {
            if (! dir.mkdirs()) {
                throw new JAXRPCException("Unable to create directory: " + dirName);
            }
        }
        if (! dir.canWrite()) {
            throw new JAXRPCException(
                "Don't have write permission for " + dirName);
        }
    }

    private String getFileName(SOAPMessage request) 
        throws SOAPException
    {
        SOAPBody body =
            request.getSOAPPart().getEnvelope().getBody();
        Object obj = body.getChildElements().next();
        SOAPElement opElem = (SOAPElement) obj;
        SOAPElement paramElem = (SOAPElement)opElem.getChildElements().next();
        return paramElem.getValue();
    }

    private void copyFile(InputStream is, OutputStream os) 
        throws IOException
    {
        byte [] b = new byte[8192];
        int nr;
        while ((nr = is.read(b)) != -1) {
            os.write(b, 0, nr);
        }
    }
    public boolean handleRequest(MessageContext mc) {
        SOAPMessageContext ctx = (SOAPMessageContext) mc;
        SOAPMessage request = ctx.getMessage();
        if (request.countAttachments() == 0) {
            throw new JAXRPCException("** Expected attachments");
        }
        try {
            Iterator it = request.getAttachments();

            while(it.hasNext()) {
                AttachmentPart part = (AttachmentPart) it.next();
                String fileName = getFileName(request);
                System.out.println("Received file named: " + fileName);

                File outFile = new File(dir, fileName);
                OutputStream os = null;
                InputStream  is = null;

                try {
                    os = new FileOutputStream(outFile);
                    is = part.getDataHandler().getInputStream();

                    copyFile(is, os);

                } catch (IOException ioe) {
                   ioe.printStackTrace();
                   throw new JAXRPCException("Exception writing file " + fileName, 
                   ioe);
                } finally {
                    try { 
                        if (is != null) is.close(); 
                    } catch (IOException ignore) {}

                    try {
                        if (os != null) os.close();
                    } catch (IOException ignore) {}
                }
            }
        } catch (SOAPException e) {
            e.printStackTrace();
            throw new JAXRPCException(e);
        }
        return true;
    }
    public QName[] getHeaders() {
        // TODO Auto-generated method stub
       return null;
    }
}


24.2.6.2. Implementing a Web Service Back-end

The following is a web service back-end implementation class that is executed when the message handler receives a file. It sends out a confirmation message to the client.

[Example 24.3] << FileTransfer.java >>

package filetransfer;

public class FileTransfer implements FileTransferIF{

    public String receiveFile(String s) {
        return "Received file named: " + s;
    }
}


The following is an example of implementing an Service Endpoint Interface (SEI).

[Example 24.4] << FileTransferIF.java >>

package filetransfer;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface FileTransferIF extends Remote {    
    public String receiveFile(String s) throws RemoteException;
}


24.2.6.3. Configuring Web Services DD File, and Instantiating and Deploying Web Services

The following is an example of the web service DD File (webservices.xml).

[Example 24.5] << webservices.xml >>

<?xml version="1.0"?>
<webservices version="1.1" xmlns="http://java.sun.com/xml/ns/j2ee">
    <webservice-description>
        <webservice-description-name>
            FileAttachmentService
        </webservice-description-name>
        <wsdl-file>
            WEB-INF/wsdl/FileAttachmentService.wsdl
        </wsdl-file>
        <jaxrpc-mapping-file>
            WEB-INF/FileAttachmentService-mapping.xml
        </jaxrpc-mapping-file>
        <port-component>
            <port-component-name>FileAttPort</port-component-name>
            <wsdl-port xmlns:ns2="urn:FileAttachmentService">
                ns2:FileTransferIFPort
            </wsdl-port>
            <service-endpoint-interface>
                filetransfer.FileTransferIF
            </service-endpoint-interface>
            <service-impl-bean>
                <servlet-link>FileAttachmentServlet</servlet-link>
            </service-impl-bean>
            <handler>
                <handler-name>ServerAttachmentHandler</handler-name>
                <handler-class>
                    filetransfer.ServerAttachmentHandler
                </handler-class>
                <init-param>
                    <param-name>directory</param-name>
                    <param-value>/temp</param-value>
                </init-param>
            </handler>
        </port-component>
    </webservice-description>
</webservices>


After configuring the webservices.xml file, configure the web.xml and jeus-webservices-dd.xml files as in the following example.

[Example 24.6] << web.xml >>

<?xml version="1.0"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
    <servlet>
        <servlet-name>FileAttachmentServlet</servlet-name>
        <servlet-class>filetransfer.FileTransfer</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>FileAttachmentServlet</servlet-name>
        <url-pattern>/FileAttachmentService</url-pattern>
    </servlet-mapping>
</web-app>


[Example 24.7] << jeus-webservices-dd.xml >>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jeus-webservices-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    <service>
        <webservice-description-name>
            FileAttachmentService
        </webservice-description-name>
        <port>
            <port-component-name>FileAttPort</port-component-name>
        </port>
    </service>
</jeus-webservices-dd>

After all deployment descriptors are configured, package them into an EAR file and deploy it to JEUS server.

The following is an example of a web service address.

http://localhost:8088/FileAttachmentService/FileAttachmentService

24.2.6.4. Implementing a Client-Side Handler

The client handler processes the message context with the handleRequest() method, and adds attachments to a SOAP message by using SAAJ.

[Example 24.8] << ClientAttachmentHandler.java >>

package filetransfer;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;

import javax.xml.rpc.handler.GenericHandler;

public final class ClientAttachmentHandler 
  extends GenericHandler
{
    private String getFileName(SOAPMessage request) 
        throws SOAPException
    {
        SOAPBody body = request.getSOAPPart().getEnvelope().getBody();

        SOAPElement opElem = (SOAPElement)body.getChildElements().next();

        SOAPElement paramElem = (SOAPElement)opElem.getChildElements().next();
        return paramElem.getValue();
    }

    public boolean handleRequest(MessageContext mc) {

        SOAPMessageContext ctx = (SOAPMessageContext) mc;
        SOAPMessage request = ctx.getMessage();

        try {
            String fileName = getFileName(request); 
            AttachmentPart part = request.createAttachmentPart();
            part.setContentType("application/x-zip-compressed");
            FileDataSource fds = new FileDataSource(fileName);
            part.setDataHandler(new DataHandler(fds));
            request.addAttachmentPart(part);
        } catch(SOAPException e) {
            e.printStackTrace();
            throw new JAXRPCException(e);
        }
        return true;
    }

    public QName[] getHeaders() {
        // TODO Auto-generated method stub
        return null;
    }    
}


24.2.6.5. Implementing a Web Service Client

The implemented client-side handler must be registered in the handler registry. The client handler includes the implementation for sending a file.

[Example 24.9] << Client.java >>

package filetransfer;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.Stub;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.HandlerRegistry;

import FileAttachmentService_pkg.*;

public final class Client {

    public static void main(String[] args) throws Exception {

       Service svc = new FileAttachmentService_Impl();
       HandlerRegistry registry = svc.getHandlerRegistry();
       List list = new ArrayList();
       list.add(new HandlerInfo(ClientAttachmentHandler.class, null, null));
    
       QName portName = new QName("urn:FileAttachmentService","FileTransferIFPort");
       registry.setHandlerChain(portName, list);

       FileTransferIF port = 
                     ((FileAttachmentService_Impl)svc).getFileTransferIFPort();
       String result = port.receiveFile("File_send.txt");
       System.out.println("** File transfer result: "+result);
    }
}


24.2.6.6. Executing a Client

A client stub must be generated to execute the previous example. Run the following command to generate the stub.

JEUS_HOME/samples/webservice/jaxrpc_saaj_msg_handler/fileAttachment/fileAttachment-c
lient$ ant build

Execute the client.

JEUS_HOME/samples/webservice/jaxrpc_saaj_msg_handler/fileAttachment/fileAttachment-c
lient$ ant run

The following result will be displayed.

** File transfer result: Received file named: File_send.txt

The previous result shows that a file named “File_send.txt” has been successfully transmitted to the server, and the file is saved under the '/temp' directory as specified in the webservices.xml file.