Thursday, February 18, 2010

How to use custom SOAP header in JAX-WS using SOAPHandler (Weblogic 10.3)




How to use custom SOAP header in JAX-WS using SOAPHandler (Weblogic 10.3)

by
Sujan Deb,PMP


Overview:

If you are struggling with SOAP Handlers and complaining about why JAX-WS does not provide us a simple way to set the SOAP Header in a web service call, then this article is for you.


Use cases:
  1. We want to pass data in a custom SOAP header which is not defined in the service WSDL.
  2. We would like to have a generic framework, which can log the complete SOAP xml request and response at client and server side respectively.
  3. We also want to log the response time of the service calls, both at client and the server side.

Assumptions:
  1. You have a basic knowledge of Java, JEE, SAAJ and Web Servivces using JAX-WS.
  2. You also have a basic idea about SOAP Handlers for JAX-WS. There are many tutorial available on this topic.
  3. I have only tested this in Weblogic server 10.3. But you can try it in other servers and let us know in case of any issue.
Solution:

This design contains two Soap Handlers, one each at client side and server side. The following figure explains the sequence of a service call:
How to use custom SOAP header in JAX-WS using SOAPHandler (Weblogic 10.3)


  1. The client programmatically configures the “ClientSideSoapHandler103” before making the service call.
  2. The client creates a custom XML to be used in the SOAP Header using SAAJ API. Client stores it in the RequestContext.
  3. The Client makes the service call.
  4. JAX-WS framework automatically copies the XML data from RequestContext to SOAPMessageContext.
  5. The “ClientSideSoapHandler103” intercepts the SOAP Request in the client side. It retrieves the XML data from SOAPMessageContext and injects it in the SOAP Request Envelop. SOAP call goes to the server side.
  6. The “ServerSideSoapHandler103” intercepts the SOAP Request in the server side before it reaches the actual webservice method. It extracts the custom XML data from the SOAP Request Envelop and stores it in the SOAPMessageContext.
  7. JAX-WS framework copies this data from SOAPMessageContext to WebServiceContext.
  8. The SOAP Request reaches the actual web service method. It retrieves the SOAP header from WebServiceContext.
  9. Web service method can now process this XML data of the SOAP header and store it back in the WebServiceContext. The web service method returns the call to the client.
  10. JAX-WS framework copies this data from WebServiceContext to SOAPMessageContext.
  11. The “ServerSideSoapHandler103” intercepts the SOAP Response. It retrieves the custom SOAP Header from “SOAPMessageContext” and injects it in the SOAP Response Envelop. The SOAP Response goes back to the client.
  12. The “ClientSideSoapHandler103” intercepts the SOAP Response in the client side. It extracts the custom SOAP Header from the SOAP response and stores it in the SOAPMessageContext.
  13. JAX-WS framework copies this data from SOAPMessageContext to the ResponseContext.
  14. The Client can read this custom XML data from the ResponseContext.

Client-side Instruction: 
  1. Use the genericSoapHandler103jar in the CLASSPATH.
  2. Use the following to configure the soap handler and set a custom SOAP Header:
String wsdl = "http://localhost:7001/HelloWorldWS/HelloWorldImplService?WSDL";
String targetNameSpace = "http://service.ws.logikr.com/";
String portName = "HelloWorldImplPort";
String serviceName = "HelloWorldImplService";
QName serviceQName = new QName(targetNameSpace, serviceName);
URL wsdlLoc = new URL(wsdl);
HelloWorldImplService service = new HelloWorldImplService(wsdlLoc,serviceQName);
HelloWorldImpl port = service.getHelloWorldImplPort();
Binding binding = ((BindingProvider) port).getBinding();
List handlerList = binding.getHandlerChain();
handlerList.add(new ClientSideSoapHandler103());
binding.setHandlerChain(handlerList);
// Soap header - Start
SoapHandlerUtil103.setSoapHeader(((BindingProvider) port).getRequestContext(), createSoapHeader());
// Soap header - End
String result = port.getUpperCase("Sujan calling from 10.3");
System.out.println("Result: " + result);

Here is the method used for creating a custom SOAP header:

private static SOAPHeader createSoapHeader() throws Exception{
SOAPHeader soapHeader = null;
try {
      soapHeader = SoapHandlerUtil103.createEmptyHeader();
      QName customHeader = new QName(targetNameSpace, "customHeader");
      SOAPHeaderElement headerElement = soapHeader.addHeaderElement(customHeader);
      headerElement.addTextNode("this is a custom header");
} catch (SOAPException ex) {
      ex.printStackTrace();
}
return soapHeader;
}



Here is the custom SOAP header it will create:
<customHeader>this is a custom header customHeader>



Server-side Instruction:
  1. Use the genericSoapHandler103jar in the CLASSPATH (in your application lib).
  2. Copy the “server-handler-chain.xml” in the same package as your web service class.
  3. Use the following annotation in the web service class to configure the Handler:      @HandlerChain(file="server-handler-chain.xml")
  4. Use the following code to read the customer SOAP Header, process it and set it back in the response:
@WebService
@HandlerChain(file="server-handler-chain.xml")
public class HelloWorldImpl {
      static String targetNameSpace = "http://service.ws.logikr.com/";
@Resource
WebServiceContext wsContext;
@WebMethod
public String getUpperCase(String msg) {
      String result = "msg is null";
      try{
            result = msg.toUpperCase();
            SOAPHeader soapHeader = SoapHandlerUtil103.getSoapHeader(wsContext);
            SoapHandlerUtil103.setSoapHeader(wsContext, updateSoapHeader(soapHeader));
            System.out.println("result: " + result);
            }catch (Exception e) {
            e.printStackTrace();
      }
      return result;
}

private SOAPHeader updateSoapHeader(SOAPHeader soapHeader) {
      if (null != soapHeader) {
      try {
            String customHeaderMsg = "";
            QName customHeader = new QName(targetNameSpace, "customHeader");
            Iterator iterator = soapHeader.getChildElements(customHeader);
            while (iterator.hasNext()) {
            Object elem = iterator.next();
            if (elem instanceof SOAPElement) {
                  SOAPElement soapElement = (SOAPElement) elem;
                  customHeaderMsg = soapElement.getTextContent();
            }
            }
            soapHeader = SoapHandlerUtil103.createEmptyHeader();
            SOAPHeaderElement headerElement = soapHeader
                        .addHeaderElement(customHeader);
            headerElement.addTextNode(customHeaderMsg.toUpperCase());
            } catch (Exception ex) {
                  ex.printStackTrace();
            }
            }
            return soapHeader;
      }
}





Detailed explanation of the Handlers:

In progress...



Conclusion:

We have created this generic framework which can be used by both client and server side code without modification.



Resources:

I will upload the "genericSoapHandler103jar " and the zip file containing the eclipse workspace soon. Let me know if you need it urgently.


Please leave your comment, if you like it or suggest any improvement.
-sd

2 comments:

  1. Hi,
    Can you please give me the jar link as soon as possible.

    Thanks & Regards,
    Praveen Kushwah


    ReplyDelete
  2. hello,
    may I receive your jar and project files ?

    thankyou
    sunnymean@gmail.com

    ReplyDelete