Bo's blog

Thursday, March 25, 2010

Use the soap handler to intercept the request/response

here is the source code, it intercept the fault response message by adding the type for some tags:

1. add annotation @HandlerChain (file = "soaphandlerchain.xml") to the class which has @WebService annotation

2. put the soaphandlerchain.xml as the same package as the web service impl class.

3. implement the handler descripted in the soaphandlerchain.xml

Hello.java

package helloservice.endpoint;
import javax.ejb.Stateless;
import javax.jws.HandlerChain;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
@WebService (serviceName = "services", portName = "hello", targetNamespace = "http://mytest.com")
@Stateless (mappedName = "HelloEjb")
@HandlerChain (file = "soaphandlerchain.xml")
public class Hello
{
  private String message = new String ("Hello, ");
  public void Hello ()
  {
  }
  @WebMethod
  public String sayHello (String name) throws MyException
  {
    if (name == null || name.isEmpty ())
    {
      final String msg = "The name cannot be null.";
      MyExceptionBean faultBean = new MyExceptionBean ();
      faultBean.setErrorCode ("E60001");
      throw new MyException (msg, faultBean);
    }
    return message + name;
  }
}


now it is the xml file soaphandlerchain.xml, this file must be at the same package as the Hello.java


<?xml version="1.0" encoding="UTF-8"?>
<jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
  <jws:handler-chain>
    <jws:handler>
      <jws:handler-name>HelloSoapHandler</jws:handler-name>
      <jws:handler-class>helloservice.endpoint.HelloSoapHandler</jws:handler-class>
    </jws:handler>
  </jws:handler-chain>
</jws:handler-chains>


now it is the SOAP Handler class (changed based on http://blog.jdevelop.eu/2008/03/08/how-to-modify-jax-ws-soap-messages/)



package helloservice.endpoint;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/**
 * This class handles the SOAP-Requests before they reach the
 * Web Service Operation. It is possible to read and manipulate
 * the SOAP-Message.
 *
 */
public class HelloSoapHandler implements SOAPHandler<SOAPMessageContext>
{
  /**
   * Is called after constructing the handler and before executing any othe method.
   */
  @PostConstruct
  public void init ()
  {
  }
  /**
   * Returns the <code>Set</code> of supported SOAP headers
   */
  public Set<QName> getHeaders ()
  {
    return null;
  }
  /**
   * Returns the message encoding (e.g. utf-8)
   *
   * @param msg
   * @return
   * @throws javax.xml.soap.SOAPException
   */
  private String getMessageEncoding (SOAPMessage msg) throws SOAPException
  {
    String encoding = "utf-8";
    if (msg.getProperty (SOAPMessage.CHARACTER_SET_ENCODING) != null)
    {
      encoding = msg.getProperty (SOAPMessage.CHARACTER_SET_ENCODING)
          .toString ();
    }
    return encoding;
  }
  /**
   * Dump SOAP Message to console
   *
   * @param msg
   */
  private void dumpSOAPMessage (SOAPMessage msg)
  {
    if (msg == null)
    {
      System.out.println ("SOAP Message is null");
      return;
    }
    System.out.println ("");
    System.out.println ("--------------------");
    System.out.println ("DUMP OF SOAP MESSAGE");
    System.out.println ("--------------------");
    try
    {
      ByteArrayOutputStream baos = new ByteArrayOutputStream ();
      msg.writeTo (baos);
      System.out.println (baos.toString (getMessageEncoding (msg)));
      // show included values
      String values = msg.getSOAPBody ().getTextContent ();
      System.out.println ("Included values:" + values);
    }
    catch (Exception e)
    {
      e.printStackTrace ();
    }
  }
  /**
   * This method handles the incoming and outgoing SOAP-Message.
   * It's an excellent point to manipulate the SOAP.
   *
   * @param SOAPMessageContext
   * @return boolean
   */
  public boolean handleMessage (SOAPMessageContext context)
  {
    //Inquire incoming or outgoing message.
    boolean outbound = (Boolean) context
        .get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    try
    {
      if (outbound)
      {
        // OUTBOUND
        System.out.println ("Direction=outbound (handleMessage)");
        SOAPMessage msg = ((SOAPMessageContext) context).getMessage ();
        // get SOAP-Part
        SOAPPart sp = msg.getSOAPPart ();
        //edit Envelope
        SOAPEnvelope env = sp.getEnvelope ();
        // add namespaces
        env.addNamespaceDeclaration ("xsd", "http://www.w3.org/2001/XMLSchema");
        env.addNamespaceDeclaration ("xsi",
            "http://www.w3.org/2001/XMLSchema-instance");
        env.addNamespaceDeclaration ("soap",
            "http://schemas.xmlsoap.org/soap/envelope");
        // get the SOAP-Body
        SOAPBody body = env.getBody ();
        // add an additional Element to the SOAP-Body
        SOAPBodyElement bodyElement = body.addBodyElement (new QName (
            "http://http://www.comverse.com/msg", "testMessage"));
        bodyElement.addTextNode ("test message");
        // print SOAP-Message
        dumpSOAPMessage (msg);
      }
      else
      {
        // INBOUND
        System.out.println ("Direction=inbound (handleMessage)");
        SOAPMessage msg = ((SOAPMessageContext) context).getMessage ();
        dumpSOAPMessage (msg);
      }
    }
    catch (Exception e)
    {
      //All other unhandled problems.
      e.printStackTrace ();
    }
    return true;
  }
  /**
   * Handles SOAP-Errors.
   *
   * @param context
   * @return
   */
  public boolean handleFault (SOAPMessageContext context)
  {
    System.out.println ("ServerSOAPHandler.handleFault");
    boolean outbound = (Boolean) context
        .get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    if (outbound)
    {
      System.out.println ("Direction=outbound (handleFault)");
      SOAPMessage msg = ((SOAPMessageContext) context).getMessage ();
      // get SOAP-Part
      SOAPPart sp = msg.getSOAPPart ();
      //edit Envelope
      SOAPEnvelope env;
      try
      {
        env = sp.getEnvelope ();
        // add namespaces
        env.addNamespaceDeclaration ("xsd", "http://www.w3.org/2001/XMLSchema");
        env.addNamespaceDeclaration ("xsi",
            "http://www.w3.org/2001/XMLSchema-instance");
        env.addNamespaceDeclaration ("soap",
            "http://schemas.xmlsoap.org/soap/envelope");
        // get the SOAP-Body
        SOAPBody body = env.getBody ();
        Iterator iter1 = body.getChildElements ();
        while (iter1.hasNext ())
        {
          SOAPBodyElement bodyElement = (SOAPBodyElement) iter1.next ();
          Iterator iter2 = bodyElement.getChildElements ();
          while (iter2.hasNext ())
          {
            Object node = iter2.next ();
            correctElement (node);
          }
        }
      }
      catch (SOAPException e)
      {
        // TODO Auto-generated catch block
        e.printStackTrace ();
      }
      // print SOAP-Message
      dumpSOAPMessage (msg);
    }
    else
    {
      System.out.println ("Direction=inbound (handleFault)");
    }
    SOAPMessage msg = ((SOAPMessageContext) context).getMessage ();
    dumpSOAPMessage (msg);
    if (!outbound)
    {
      try
      {
        if (context.getMessage ().getSOAPBody ().getFault () != null)
        {
          String detailName = null;
          try
          {
            detailName = context.getMessage ().getSOAPBody ().getFault ()
                .getDetail ().getFirstChild ().getLocalName ();
            System.out.println ("detailName=" + detailName);
          }
          catch (Exception e)
          {
          }
        }
      }
      catch (SOAPException e)
      {
        e.printStackTrace ();
      }
    }
    return true;
  }
  static Map<String, String> tagMap;
  {
    tagMap = new HashMap ();
    tagMap.put ("errorCode", "xsd:string");
    tagMap.put ("faultstring", "xsd:string");
    tagMap.put ("ns4:fault", "ns4:MyException");
  }
  private void correctElement (Object node)
  {
    System.out.println ("Node: " + node);
    SOAPElement child;
    try
    {
      child = (SOAPElement) node;
    }
    catch (Exception e)
    {
      return;
    }
    String tagName = child.getTagName ();
    if (tagMap.containsKey (tagName))
    {
      try
      {
        child.addAttribute (new QName (
            "http://www.w3.org/2001/XMLSchema-instance", "type", "xsi"),
            tagMap.get (tagName));
      }
      catch (SOAPException e)
      {
        // TODO Auto-generated catch block
        e.printStackTrace ();
      }
    }
    Iterator iter = child.getChildElements ();
    while (iter.hasNext ())
    {
      node = iter.next ();
      correctElement (node);
    }
  }
  public void close (MessageContext messageContext)
  {
  }
  /**
   * Is executed before this handler is being destroyed -
   * means after close() has been executed.
   */
  @PreDestroy
  public void destroy ()
  {
  }
}

the exception class: MyException.java (ref: http://io.typepad.com/eben_hewitt_on_java/2009/07/using-soap-faults-and-exceptions-in-java-jaxws-web-services.html)

package helloservice.endpoint;
import java.io.Serializable;
import javax.xml.ws.WebFault;
@WebFault (name = "fault", targetNamespace = "http://myexceptions.test.com")
public class MyException extends Exception implements Serializable
{
  private static final long serialVersionUID = -4552704108896976212L;
  /**
   * Java type that goes as soapenv:Fault detail element.
   */
  private MyExceptionBean faultInfo;
  public MyException (String message, MyExceptionBean faultInfo)
  {
    super (message);
    this.faultInfo = faultInfo;
  }
  public MyException (String message, MyExceptionBean faultInfo, Throwable cause)
  {
    super (message, cause);
    this.faultInfo = faultInfo;
  }
  public MyExceptionBean getFaultInfo ()
  {
    return faultInfo;
  }
}


and the bean class: MyExceptionBean.java:



package helloservice.endpoint;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType (XmlAccessType.FIELD)
@XmlType (name = "MyException", propOrder =
{ "errorCode" })
public class MyExceptionBean
{
  private String errorCode = "654321";
  public String getErrorCode ()
  {
    return errorCode;
  }
  public void setErrorCode (String errorCode)
  {
    this.errorCode = errorCode;
  }
}

here is the pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>TestWS</groupId>
  <artifactId>testws</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxws-maven-plugin</artifactId>
        <version>1.10</version>
        <executions>
          <execution>
            <goals>
              <goal>wsgen</goal>
            </goals>
            <configuration>
              <sei>helloservice.endpoint.Hello</sei>
              <!--for demo purpose only, the webapp does not-->
              <!--need the generated wsdl files-->
              <genWsdl>true</genWsdl>
              <keep>true</keep>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <pluginRepositories>
    <pluginRepository>
      <id>maven2-repository.dev.java.net</id>
      <url>http://download.java.net/maven/2/</url>
    </pluginRepository>
  </pluginRepositories>
  <dependencies>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>6.0</version>
    </dependency>
    <dependency>
      <groupId>xalan</groupId>
      <artifactId>xalan</artifactId>
      <version>2.7.0</version>
    </dependency>
  </dependencies>
</project>

1 Comments:

At 3:37 AM, Blogger Atul said...

Thanks.. it is really helpful for me. I was trying to log inbound and outbound messages using interceptors. But I was failed to log the outbound messages. This really works.

 

Post a Comment

<< Home