The easiest way to create a new handler is to extend BasicHandler and implement the invoke method. The following example does just that and removes all nnn; entities from the XML.
package org.bensjavaspot.handlers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.soap.SOAPException;
import javax.xml.transform.stream.StreamSource;
import org.apache.axis.AxisFault;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.axis.SOAPPart;
/**
* Custom Axis handler that allows us to intercept the incoming input stream and
* remove any bad characters before passing it back into the chain.
*
* @author Ben Hills
*/
public class SOAPCleanerHandler extends BasicHandler
{
private static final Log log = LogFactory.getLog(SOAPCleanerHandler.class);
public void invoke(MessageContext mtx) throws AxisFault {
/* Get the SOAP Message */
Message message = mtx.getResponseMessage();
if (message != null) {
try {
/* Message normally consists of single part and optionally attachments. We just need the main part */
SOAPPart part = (org.apache.axis.SOAPPart) message.getSOAPPart();
/* Get the source */
StreamSource source = (StreamSource) part.getContent();
/* Get the input XML as a string */
String messageAsString = message.getSOAPPartAsString();
/* Regexp utils need a StringBuffer so create new one with input string */
StringBuffer out = new StringBuffer(messageAsString);
/* Create regular expresion to remove all nnn; type entities */
Pattern pat = Pattern.compile("&#[0-9]*;");
Matcher matcher = pat.matcher(out.toString());
StringBuffer sb = new StringBuffer();
/* Replace each match found with a single space */
while (matcher.find()) {
matcher.appendReplacement(sb, " ");
}
matcher.appendTail(sb);
/* Convert back to byte array using the correct encoding */
ByteArrayInputStream b = new ByteArrayInputStream(sb.toString().getBytes(part.getEncoding()));
/* Pass input stream back in to chain */
source.setInputStream(b);
/* Set it again */
part.setContent(source);
}
catch (SOAPException ex) {
log.error("Failed to get and set source", ex);
}
catch (IOException ex) {
log.error("Error reading input stream", ex);
}
}
}
}
You can download the source code here
Once we have created our handler we need to instruct Axis to insert it into the response chain. This is achieved by adding an entry into the Axis client-config.wsdd file
<globalConfiguration >
<responseFlow >
<handler type="java:org.bensjavaspot.handlers" />
</responseFlow >
</globalConfiguration >
And that's it!
Excellent blog entry Ben.
ReplyDeleteI'm struggling with the same issue presently.
I'm however trying to implement the handler in JAX-WS 2 that comes with JDK-1.6. But I can't figure out how to get the payload without the JAXBContext object trying to parse the XML.
Have you looked at a similar implementation in JAX-WS 2?
Hi Jan,
ReplyDeleteSorry about the very late reply, I've only just noticed this comment! I'm afraid to say, no I haven't tried to implement a handler in JAX-WS as my current web service development is still on AXIS and we haven't moved to JAX-WS (yet). If you get to the bottom of the issue I would be very interested in hearing about your solution.
Ben.