Wednesday, April 11, 2018

Java SOAP WebService Proxy: SOAPHandler to print SOAP messages

Problem: We all know about creating webservice proxy from jdeveloper. Proxy generates few classes based on WSDL and associated XSD and allow us to invoke webservice as if we are simply calling some java method. Complexity of creating soap message and sending over network and receiving output is hidden from us. One of the problem we face many a time is to view SOAP request and response messages. If we can it would be very helpful for debugging.
In this blog I will be using SOAP handler to view SOAP messages.

Solution: Solution which I am going to show is using SOAP handler feature of web-service proxy.

Using proxy is very simple, we just work with normal java classes and complexity of invoking webservice is completely hidden. SOAP Handler is java class (with some guidelines) that can be injected with proxy and proxy will make sure to call methods of this class before sending a request and after receiving message.

SOAP Handler class must implement SOAPHandler<SOAPMessageContext> class.
A Sample SOAPHandler class can look like

import java.util.Collections;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class CalculatorSOAPHandler implements SOAPHandler<SOAPMessageContext> {
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    public boolean handleMessage(SOAPMessageContext messageContext) {
                Boolean outboundProperty = (Boolean)
            messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if (outboundProperty.booleanValue()) {
            System.out.println("Request message:");
        } else {
            System.out.println("Response message:");
        }
        
        try {
            System.out.println(messageContext.getMessage().getSOAPPart().getEnvelope().toString());
        } catch (SOAPException e) {
            e.printStackTrace();
        }


        return true;
    }

    public boolean handleFault(SOAPMessageContext messageContext) {
        return true;
    }

    public void close(MessageContext messageContext) {
    }
}


Now we need to inject this class in proxy or in other words we need to configure it with proxy so that it knows about this class. There are couple of ways to do that.
1. Specify SOAP handler while creating proxy in Proxy creating wizard.

In above diagram we are specifying a SOAP handler. We have not selected any port so handler will be registered against all web-service port. Jdev will not create any class. You must create class separately.

When we generate proxy, there is one most important class which has @WebServiceClient annotation. Above step will create a handler entry in that class as

@HandlerChain(file = "MyServiceProxy-HandlerChain.xml")

It will also create a HandlerChain.xml file, which will have entry of handler.
<ns0:handler-chains xmlns:ns0="http://java.sun.com/xml/ns/javaee">
    <ns0:handler-chain>
        <ns0:service-name-pattern xmlns:ns1="http://xmlns.oracle.com/SIH/SIHParentProcess/BPELParentProcess">ns1:bpelparentprocess_client_ep</ns0:service-name-pattern>
        <ns0:handler>
            <ns0:handler-name>CalculatorSOAPHandler</ns0:handler-name>
            <ns0:handler-class>com.san.wsproxy.client.CalculatorSOAPHandler</ns0:handler-class>
        </ns0:handler>
    </ns0:handler-chain>
</ns0:handler-chains>



Now when you run service you will see that handleMessage method of SOAP handler is getting called and its printing SOAP header and body.

Another way to associate SOAP handler class with proxy is simply modify client code.
2. With proxy generation you get a client class. It has a main method, which shows how to run a webservice. You can use this class to add security (WS-Security) header in soap message by setting OWSM policy. Similarly you can use this class also to associate handler with proxy.

Your client method would look something like

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        CalculatorSoap calculatorSoap = calculator.getCalculatorSoap();
        // Add your code to call the desired methods.
       
        Binding binding = ((BindingProvider)calculatorSoap).getBinding();
        List handlerList = binding.getHandlerChain();
        handlerList.add(new CalculatorSOAPHandler());
        binding.setHandlerChain(handlerList);
             
             
        System.out.println(calculatorSoap.add(5, 6));
       
    }

Thats all, Now if you run service, you will see that SOAP messages are getting printed on console.



Below is the print of messages
Calling handleMessage
<env:Header xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"/>

<S:Body xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <Add xmlns="http://tempuri.org/">
    <intA>5</intA>
    <intB>6</intB>
  </Add>
</S:Body>

Calling handleMessage
<env:Header xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"/>

<soap:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <AddResponse xmlns="http://tempuri.org/">
    <AddResult>11</AddResult>
  </AddResponse>
</soap:Body>


Instead system.out.println, you can add ADF logger also to print messages. In that case your messages would appear in weblogic log as well.