Message Handlers

The Novell exteNd WSSDK supports the JAX-RPC APIs from Sun Microsystems. One of the JAX-RPC APIs is the SOAP Message Handler API. This API can be used for Encryption and Decryption, Authentication, Authorization, Logging, Auditing, etc.

A handler is a special piece that conceptually sits between the client or server and the transport layer. A handler gets a chance to do something to the message right before or right after the message is put on the wire. Handlers can manipulate all aspects of SOAP messages using the SAAJ API. The picture below illustrates the concept of handlers.

This example illustrates how to write a message handler, which can compress a file that is being sent as part of the SOAP message. This Web service simply echoes a file, which gets compressed on the wire.

WSDL for File Transfer Service

Below is the WSDL for the service.

<?xml version="1.0" encoding="utf-8"?>
<definitions 
  xmlns="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
  targetNamespace="http://localhost/zipservice/" >
  <message name="EchoFileSoapIn">
    <part name="parameter_in" type="xsd:base64Binary"/>
  </message>
  <message name="EchoFileSoapOut">
    <part name="parameter_out" type="xsd:base64Binary"/>
  </message>
  <portType name="ZipFileSoap">
    <operation name="EchoFile">
      <input message="EchoFileSoapIn"/>
      <output message="EchoFileSoapOut"/>
    </operation>
  </portType>
  <binding name="ZipFileSoapBinding" type="ZipFileSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
    <operation name="EchoFile">
      <soap:operation soapAction="http://localhost/zipservice/EchoFile" style="rpc"/>
      <input>
        <soap:body use="encoded" 
          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </input>
      <output>
        <soap:body use="encoded" 
          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
    </operation>
  </binding>
  <service name="ZipFileService">
    <port binding="ZipFileSoapBinding" name="ZipFileSoapPort">
      <soap:address location="http://localhost:8080/services/zipfile"/>
    </port>
  </service>
</definitions>

The WSDL defines an operation called EchoFile which has a base64Binary parameter as input and a base64Binary as output.

The Message Handlers

The ZipFileClientHandler class is a JAX-RPC handler. It implements the javax.xml.rpc.Handler interface. When a client invokes an operation, the handleRequest method is called by the runtime to process the client request SOAP message. Once the message is processed by the handler, the request is sent to the server.

When the response from the server is received, the WSSDK runtime invokes the handleResponse method of the handler, which then processes the response. Finally, the values of the output parameters are returned to the client. The ZipFileClientHandler class compresses the file being sent out using GZIPOutputStream in the handleRequest method and it decompresses the echoed value using GZIPInputStream.

The Base64Encoded class is a utility class provided by the Novell exteNd WSSDK for converting a BASE64 encoded byte array into a string and vice versa. Please note that the handleRequest method of the client handler prints the compressed message to the console, which means that this will show as output when you run the client application.

package handler;
                                                                           
import java.io.*;
                                                                           
import java.util.*;
import java.util.zip.*;
                                                                           
import javax.xml.soap.*;
import javax.xml.rpc.handler.*;
import javax.xml.rpc.handler.soap.*;
                                                                           
import com.sssw.jbroker.web.ServiceException;
import com.sssw.jbroker.util.Base64Encoder;
                                                                           
public class ZipFileClientHandler implements Handler
{
    public boolean handleRequest(MessageContext context)
    {
    |   try
    |   {
    |   |   SOAPMessageContext smc = (SOAPMessageContext)context; 
    |   |   SOAPMessage msg = smc.getMessage(); 
    |   |   SOAPPart sp = msg.getSOAPPart(); 
    |   |   SOAPEnvelope se = sp.getEnvelope(); 
    |   |                                                                  
    |   |   // next step based on the processing model for this handler   
    |   |   SOAPBody body = se.getBody();
    |   |   Iterator it = body.getChildElements();
    |   |   SOAPElement opElem = (SOAPElement) it.next();
    |   |   it = opElem.getChildElements();
    |   |   SOAPElement pin = (SOAPElement) it.next();
    |   |   it = pin.getChildElements();
    |   |   Text textNode = (Text) it.next();
    |   |   textNode.detachNode();
    |   |                                                                  
    |   |   String encContent = textNode.getValue();
    |   |   byte[] contentBytes = Base64Encoder.decode(encContent);
    |   |                                                                  
    |   |   // zip it
    |   |   ByteArrayOutputStream baos = new ByteArrayOutputStream();
    |   |   GZIPOutputStream zos = new GZIPOutputStream(baos);
    |   |   zos.write(contentBytes);
    |   |   zos.flush();
    |   |   zos.finish();
    |   |   zos.close();
    |   |                                                                  
    |   |   byte[] zippedbytes = baos.toByteArray();
    |   |                                                                  
    |   |   String zippedContent = Base64Encoder.encode(zippedbytes);
    |   |   System.out.println("zipped-encoded content is:");
    |   |   System.out.println(zippedContent);
    |   |   pin.addTextNode(zippedContent);
    |   |   msg.saveChanges();
    |   |   return true;
    |   }
    |   catch(Throwable t)
    |   {
    |   |   t.printStackTrace();
    |   |   throw new ServiceException(t);
    |   }
    }
                                                                           
    public boolean handleResponse(MessageContext context)
    {
    |   try
    |   {
    |   |   SOAPMessageContext smc = (SOAPMessageContext)context; 
    |   |   SOAPMessage msg = smc.getMessage(); 
    |   |   SOAPPart sp = msg.getSOAPPart(); 
    |   |   SOAPEnvelope se = sp.getEnvelope(); 
    |   |                                                                  
    |   |   // next step based on the processing model for this handler   
    |   |   SOAPBody body = se.getBody();
    |   |   Iterator it = body.getChildElements();
    |   |   SOAPElement op = (SOAPElement) it.next();
    |   |   SOAPElement param = (SOAPElement) op.getChildElements().next();
    |   |   Text textNode = (Text) param.getChildElements().next();
    |   |   String zippedenccontent = textNode.getValue();
    |   |   System.out.println("zipped-encoded content is:");
    |   |   System.out.println(zippedenccontent);
    |   |   textNode.detachNode();
    |   |                                                                  
    |   |   byte[] zippedBytes = Base64Encoder.decode(zippedenccontent);
    |   |                                                                  
    |   |   ByteArrayInputStream bais = 
    |   |       new ByteArrayInputStream(zippedBytes);
    |   |   GZIPInputStream zis = new GZIPInputStream(bais);
    |   |   ByteArrayOutputStream baos = new ByteArrayOutputStream();
    |   |   int c = -1;
    |   |   while ((c = zis.read()) != -1) {
    |   |   |   baos.write(c);
    |   |   }       
    |   |   baos.flush();
    |   |   byte[] contentBytes = baos.toByteArray();
    |   |                                                                  
    |   |   String encContent = Base64Encoder.encode(contentBytes); 
    |   |   param.addTextNode(encContent);
    |   |   msg.saveChanges();
    |   |   return true;
    |   }
    |   catch(Throwable t)
    |   {
    |   |   t.printStackTrace();
    |   |   throw new ServiceException(t);
    |   }
    }
                                                                           
    public boolean handleFault(MessageContext context) { return true; }
                                                                           
    // Lifecycle method 
    public void init(HandlerInfo config) {}
    public void destroy() {}
    public javax.xml.namespace.QName[] getHeaders() { return null; }
}

The ZipFileServerHandler is a message handler for the server-side. It is derived from ZipFileClientHandler and does the opposite to that of the client handler.

package handler;
                                                                           
import javax.xml.rpc.handler.*;
                                                                           
public class ZipFileServerHandler extends ZipFileClientHandler
{
    public boolean handleRequest(MessageContext context)
    {
    |   return super.handleResponse(context);
    }
                                                                           
    public boolean handleResponse(MessageContext context)
    {
    |   return super.handleRequest(context);
    }    
}

The Client

The client gets access to the service using the JNDI lookup. It registers the ZipFileHandler into the registry for the port the client needs to access. Then it reads a file and calls the EchoFile method.

package handler;
                                                                           
import java.io.*;
                                                                           
import java.util.*;
                                                                           
import javax.xml.namespace.QName;
                                                                           
import javax.xml.rpc.*;
import javax.xml.rpc.handler.*;
                                                                           
public class Client
{
    public static void main(String[] args) throws Exception
    {
    |   // instantiate the service.
    |   ZipFileServiceImpl service = new ZipFileServiceImpl();  
    |                                                                      
    |   // set the handler chain
    |   QName portName = new QName("http://localhost/zipservice/", 
    |       "ZipFileSoapPort");
    |   ArrayList handlers = new ArrayList();
    |   handlers.add(new HandlerInfo(ZipFileClientHandler.class, null, null));  
    |   service.getHandlerRegistry().setHandlerChain(portName, handlers);
    |                                                                      
    |   // get access to the proxy
    |   ZipFileSoap zipfile = service.getZipFileSoapPort();
    |   // set the end point address
    |   ((Stub)zipfile)._setProperty("javax.xml.rpc.service.endpoint.address", 
    |       (args.length > 0) ? args[0] : "http://localhost:9090/zipfile");
    |                                                                      
    |                                                                      
    |   // get the content of file to be sent
    |   String content = getContent("LICENSE.txt");
    |   byte[] contentBytes = content.getBytes();
    |                                                                      
    |   // print the content to send
    |   System.out.println("sending content:");
    |   System.out.println(content);
    |                                                                      
    |   // call echo file
    |   byte[] echoedBytes = zipfile.echoFile(contentBytes);
    |                                                                      
    |   // print the content received
    |   String result = new String(echoedBytes);
    |   System.out.println("received content:");
    |   System.out.println(result);
    |                                                                      
    |   if(content.equals(result)) {
    |   |   System.out.println("echo file succedded!");
    |   } else {
    |   |   System.out.println("echo file failed!");
    |   }
    }
                                                                           
    private static String getContent(String fileName) throws IOException
    {
    |   StringBuffer contentBuffer = new StringBuffer();
    |   FileInputStream fis = new FileInputStream(fileName);
    |   int c = -1;
    |   while((c = fis.read()) != -1) {
    |   |   contentBuffer.append((char) c);
    |   }
    |   fis.close();
    |   return new String(contentBuffer);
    }
}

ZipFile Web Service Implementation

The FileTransferService instantiates the ZipFileImpl class and sets it as the target. In the init method it registers the ZipFileHandler instance for the port for which the service is defined.

As an example, if this server is deployed in the jwebserv Web Server, you have to start the Web Server like this:

   jwebserv -verbose services.war

The ZipFile service implementation is shown below:

package handler;
                                                                           
import java.util.ArrayList;
                                                                           
import javax.servlet.ServletException;
                                                                           
import javax.xml.namespace.QName;
                                                                           
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.HandlerRegistry;
import javax.xml.rpc.handler.HandlerChain;
                                                                           
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
                                                                           
public class FileTransferService extends ZipFileSoap_ServiceTieSkeleton
{
    public FileTransferService()
    {
    |   // initialize the delegate
    |   setTarget(new ZipFileImpl());
    }
                                                                           
    public void init() throws ServletException
    {
    |   super.init();
    |                                                                      
    |   try {
    |   |   // set the handler chain
    |   |   ArrayList handlers = new ArrayList();
    |   |   handlers.add(
    |   |       new HandlerInfo(ZipFileServerHandler.class, null, null));
    |   |   _setHandlerChain(handlers);
    |   |                                                                  
    |   } catch (Throwable t) {
    |   |   t.printStackTrace();
    |   |   throw new ServletException(t.getMessage());
    |   }
    }
}

Please refer to the handler API documentation for more information. For detailed information about building, deploying and running the example refer to the README.



Copyright © 2003, 2004 Novell, Inc. All rights reserved. Copyright © 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.