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

Table of Contents

23.1. Using SAAJ
23.1.1. Creating a SOAP Message
23.1.2. Handling an SAAJ Document
23.1.3. Sending a SOAP Message through SAAJ
23.2. Creating SOAP Message Handlers
23.2.1. Creating a Message Handler
23.2.2. Designing a Message Handler and a Handler Chain
23.2.3. Creating a Handler Interface
23.2.4. Configuring Java EE Web Services DD File
23.2.5. Using a SOAP Message Handler from the Client
23.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.

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.


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.

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.

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.

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.

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.

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 23.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;
    }
}


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


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



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

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

[Example 23.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;
    }    
}