Thursday, September 25, 2008

Using JAX-WS Handlers

The JAX-WS provides a good facility to do pre/post processing on SOAP messages using SOAPHandler. The handlers are useful for auditing, logging and potentialy some more functionality. In this entry I will try to explain a typical usage of handlers for logging the SOAP messages. Every handler class needs to implement javax.xml.ws.handler.soap.SOAPHandler interface as shown below-
public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {

    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    @Override
    public void close(MessageContext context) {
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        logToSystemOut(context);
        return true;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        logToSystemOut(context);
        return true;
    }

    private void logToSystemOut(SOAPMessageContext smc) {
        Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if (outboundProperty.booleanValue()) {
            System.out.println("\nOutgoing message:");
        } else {
            System.out.println("\nIncoming message:");
        }

        SOAPMessage message = smc.getMessage();
        try {
            message.writeTo(System.out);
        } catch (Exception e) {
            System.out.println("Exception in handler: " + e);
        }
    }

}
Now create a handler confiuration file say 'handlers.xml' as shown below and put it in 'WEB-INF' directory of your application.

<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
   <handler-chain>
      <handler>
         <handler-class>my.server.pkg.LoggingHandler</handler-class>
      </handler>
   </handler-chain>
</handler-chains>
Using @HandlerChain annotation we can instruct the JAX-WS runtime to apply the handlers configured in the above-mentioned configuration on a web service endpoint-
@WebService(endpointInterface = "my.server.pkg.IMyService")
@HandlerChain(file = "handlers.xml")
public class MyServiceImpl implements IMyService {
Now all incoming and outgoing SOAP messages will be logged to standard output stream. Source code for this example is available here.
For developing and deploying web services see my earlier posts-
http://blog.vinodsingh.com/2008/09/building-jax-ws-web-service.html
http://blog.vinodsingh.com/2008/09/jax-ws-web-service-and-jboss.html

19 Comments:

Anonymous said...

Very easy & useful !!
Can I use it for authentication purpose ? how ?

obaid
Dhaka

Anonymous said...

Of course why not. One can do so using-
public boolean handleMessage(SOAPMessageContext context)
method. Returning false from there will stop further processing.

Manish said...

Great Help!! I'v been searching for a help like this for the entire day and at last found this.

Anonymous said...

thanks man. this is very clear.
can i use my old jaxrpc handler with jaxws service?

Anonymous said...

Though I never tried but JAX-RPC handlers may not work directly under JAX-WS as they use different APIs. IMO porting of handlers from JAX-RPC to JAX-WS should be pretty easier.

siddhartha said...

Hi Vinod .... its been vary useful for me ... thanks ...
but i am not able to log the Exceptions ...
handleFault is not being called in case of an Exception thrown.

can u help me ??

Siddhartha.
sidd.aec@gmail.com

jR said...

Excellent job Vinod,
The code worked like a charm, the very first time! And it addressed my need perfectly.
I am bookmarking you blog for future :), keep up the good work.
Thanks a mil,
jR

Thomas said...

Thanks for the tutorial! We try to incorporate a SAML Token that we receive from another party into the header by using a JAX-WS handler.

Bit of a problem is, that we have the SAML token not as object but as plain xml string. How can we insert this into the header of the SOAP message as easy as possible?

Regards,
Thomas

James Weakley said...

Don't suppose you know whether higher-level handlers can be attached? e.g. view the raw HTTP message before the SOAP body is read by JAX-WS? I have a need to handle all messages, not just the ones that are valid.

Anonymous said...

James,

By the time handler is invoked SOAP body is already read by JAX-WS. Why don't you try filters for the task you mentioned?

James Weakley said...

Thanks Vinod.

I'm talking about the client side, and I'm calling the service from a J2SE client.

So it's the response from the server that i'm interesting in handling. Sometimes the remote server returns 500 with an HTML error page rather than SOAP. The resulting UnsupportedMediaException
(Unsupported Content-Type: text/html) doesn't have the page contents in it, which I'd like to be able to log so that we know what was returned instead of the SOAP response.

I didn't think it was possible to add filters in these situations, but hopefully I'm wrong.

Anonymous said...

James,

Handlers can be attached on client side as well, see here.

James Weakley said...

Thanks again Vinod.

I registered both SOAP and logical handlers, but the exception is already thrown before the handler is called for the response.

If it were possible to register a filter, that would probably work, however I can't find a way to access the HTTP level objects that the JAX webservice will use for the call, it appears they aren't exposed.

Appreciate your responses, thank you.

prabahar said...

Can you shed some light on how to pass the values through the messagecontext? from the client to the handler?

Srikanth K said...

Really good WORK MAN!!!!!!!!!!!!!!!!!!



THANK YOU VERY VERY MUCH

rev said...

Hi Vinod,

i am planning to create a simple web service using JBOSS + JAX-WS + Maven. Please give me steps in doing this.


Thanks

Subhani S said...

I have soap message which contain the header and it contain the node element, i am trying to get the value at server side handler but i could not...this is my code
Iterator it = soapHeader.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);

// if no header block for next actor found? throw exception
if (it == null || !it.hasNext())
{
generateSOAPErrMessage(soapMsg, "No header block for next actor.");
}

// if no User Details found? throw exception
Node userNode = (Node) it.next();
String userValue = null;
if(userNode != null)
{
System.out.println("Node Element****"+userNode);
userValue = (userValue == null) ? null : userNode.getValue();
System.out.println("Node Element after ****"+userValue);
}

Could you please any tell me how to get the node value

Jayaram Pradhan said...

Hi Vinod,

M using Sprin3.2+ JAXWS2.0+Maven

My servlet for the same is:
com.sun.xml.ws.transport.http.servlet.WSSpringServlet

I m trying to incorporate the steps, you mentioned above the for the SOAP Handler, But it seems to be no out put M getting from that.

even I tried with debugging also, but its not at all working.

Please help me on this.

Thanks,
Jayaram.
jayaramimca@gmail.com

Anonymous said...

Handler Chain is not working for JBOSS 5.1, but working for JBoss 6.0. Could you please guide what error it could be? I have used same method as you described