Enterprise Java

Logging JAX-WS SOAP messages in Spring

Whenever you’re using JAX-WS within Spring you’ll probably want to log the incoming and outgoing SOAP messages – if only for debugging during development. So the first thing to do is increase the log levels, right? Unfortunately this will have no effect. What you will have to do is to make use of the javax.xml.ws.handler.HandlerResolver interface. So how do we do this?

First of all, you’ll want to create a class that implements the HandlerResolver interface. This is a very basic class that will be used to get control over the handler chain. It could look something like the following:

A DefaultHandlerResolver

package it.jdev.example.ws;

import java.util.List;

import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;

public class DefaultHandlerResolver implements HandlerResolver {

    private List<Handler> handlerList;

    @Override
    public List<Handler> getHandlerChain(final PortInfo portInfo) {
        return handlerList;
    }

    public void setHandlerList(final List<Handler> handlerList) {
        this.handlerList = handlerList;
    }

}

We then have to implement the class that will do the actual logging. This class will have to implement the SOAPHandler<SOAPMessageContext> interface. A very simple implementation that only logs the message using SLF4J:

The LoggingHandler

package it.jdev.example.ws;

import java.io.ByteArrayOutputStream;
import java.lang.invoke.MethodHandles;
import java.util.Set;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {

    private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    @Override
    public boolean handleMessage(final SOAPMessageContext context) {
        final SOAPMessage msg = context.getMessage();
        final boolean request = ((Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY)).booleanValue();
        if (request) { // This is a request message.
            logMessage(msg);
        } else { // This is the response message
            logMessage(msg);
        }
        return true;
    }

    @Override
    public boolean handleFault(final SOAPMessageContext context) {
        logMessage(context.getMessage());
    }

    private void logMessage(final SOAPMessage msg) {
        try {
            // Write the message to the output stream
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            msg.writeTo(baos);
            LOGGER.info(baos.toString());
            baos.close();
        } catch (final Exception e) {
            LOGGER.error("Caught exception: " + e.getMessage(), e);
        }
    }

    @Override
    public void close(final MessageContext context) {
        // Not required for logging
    }

    @Override
    public Set<QName> getHeaders() {
        // Not required for logging
        return null;
    }

}

Finally, we’ll have to wire things together in Spring. Using xml configuration, all you have to do is add a new bean definition:

Configuring Spring to use the SOAP message logger

	<bean id="handlerResolver" class="it.jdev.example.ws.DefaultHandlerResolver">
		<property name="handlerList">
			<list>
				<bean class="it.jdev.example.ws.LoggingHandler" />
			</list>
		</property>
	</bean>

And that’s all there is to it. Your application should now log all JAX-WS SOAP messages.

Reference: Logging JAX-WS SOAP messages in Spring from our JCG partner Wim van Haaren at the JDev blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Luis Henrique Rocha
8 years ago

Nice article, but this piece of code does not make sense

if (request) { // This is a request message.
logMessage(msg);
} else { // This is the response message
logMessage(msg);
}

Wim van Haaren
8 years ago

The handleMessage() method handles both request and response messages. The example illustrates a way to differentiate between request and response in case you’ll want to handle them differently. If both outbound and inbound messages are handled in exactly the same way – as in this example – there indeed is no need to differentiate. In that case the method in the example could be shortened to

public boolean handleMessage(final SOAPMessageContext context) {
final SOAPMessage msg = context.getMessage();
logMessage(msg);
return true;
}

Diego Alcorta
Diego Alcorta
7 years ago

@Override
public boolean handleFault(final SOAPMessageContext context) {
logMessage(context.getMessage());
}

Should it return true or false ?

Wim van Haaren
Wim van Haaren
7 years ago
Reply to  Diego Alcorta

From the javax.xml.ws.handler.Handler javadoc:

Returns:
An indication of whether handler fault processing should continue for the current message
Return true to continue processing.
Return false to block processing.

Goutam Giri
Goutam Giri
1 year ago

Its not working for me, its not invoked my soaphandler class , I am using jaxws-spring api, and added this bean wire in applicationContext.xml , added here because my webservice is configured here. Please help why this in not invoked

Back to top button