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