Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

So you've created a JAX-WS client for a web service you want to call and you added the AddressingFeature to support WS-A. You can see wsa:MessageIDs being generated and sent to the target web service, but instead of having random IDs, you want to be able to create your own. Unfortunately, AddressingFeature doesn't provide this level of customisation.

If you're using JAX-WS RI to generate your service client, the only way to get past this shortcoming is to create a SOAPHandler that can intercept and change SOAP headers. The handler needs to look for the MessageID header, which can then be overwritten.

To get this handler to work, two other additions are required. First, a handler-chain.xml file needs to be created that specifies the name of the SOAP handler class we're about to implement. This file needs to be presents on the classpath and be part of the same package as the web service client.
 handler-chain.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-class>net.igorkromin.WsaMessageIdInjectionHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>


Then the xxxxService class that was generated as part of the web service client needs the following annotation added at class level...
 Service Annotation
@HandlerChain(file="handler-chain.xml")


Then comes the fun part, the SOAP handler class itself...


 WsaMessageIdInjectionHandler.java
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.util.Iterator;
import java.util.Set;
public class WsaMessageIdInjectionHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public Set<QName> getHeaders() {
return null;
}
@Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
Iterator iter = envelope.getHeader().getChildElements();
while (iter.hasNext()) {
SOAPHeaderElement headerElement = (SOAPHeaderElement) iter.next();
if ("MessageID".equals(headerElement.getLocalName())
&& "http://www.w3.org/2005/08/addressing"
.equals(headerElement.getNamespaceURI())) {
String wsaId = "http://example.com/my_message_id";
headerElement.setValue(wsaId);
break;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
}
}


What this handler does is first check whether it's dealing with an outbound message i.e. a message being sent to the web service. This should always be the case for a web service client, but it doesn't hurt to check anyway. Then all of the SOAP headers are checked to see if the MessageID header is one of them. Once the MessageID header is found, it's value is overwritten and set to "http://example.com/my_message_id". Note that the wsa:MessageID must be a URL since it's type is defined as xsd:anyURI in the WS-A specification. Of course you can define how the wsaId is created based on hour requirements.

I found this article and this post useful when coming up with my own solution.

-i

Skip down to comments...
Hope you found this post useful...

...so please read on! I love writing articles that provide beneficial information, tips and examples to my readers. All information on my blog is provided free of charge and I encourage you to share it as you wish. There is a small favour I ask in return however - engage in comments below, provide feedback, and if you see mistakes let me know.

If you want to show additional support and help me pay for web hosting and domain name registration, donations, no matter how small, are always welcome!

Use of any information contained in this blog post/article is subject to this disclaimer.
 
comments powered by Disqus
Other posts you may like...