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

A quick disclaimer...

Although I put in a great effort into researching all the topics I cover, mistakes can happen. Use of any information from my blog posts should be at own risk and I do not hold any liability towards any information misuse or damages caused by following any of my posts.

All content and opinions expressed on this Blog are my own and do not represent the opinions of my employer (Oracle). Use of any information contained in this blog post/article is subject to this disclaimer.
Hi! You can search my blog here ⤵
NOTE: (2022) This Blog is no longer maintained and I will not be answering any emails or comments.

I am now focusing on Atari Gamer.