TL;DR: How can I have an optional <part> in the response <message> for a wsdl service.
I am:
The problem: Depending on the version of the service there could be an additional element in the response Body element.
With the following service definition I can target v1 of the service:
<message name="CreateResponse">
<part name="ResourceCreated" element="ns7:ResourceCreated" />
</message>
And this one works with v2 of the service:
<message name="CreateResponse">
<part name="ResourceCreated" element="ns7:ResourceCreated" />
<part name="Shell" element="ns8:Shell" />
</message>
Question: How can I target both versions with the same service definition? I don't really need the second element so just ignoring it would be fine.
Details:
Windows Remote Management Service
.Create
response contains a single ResponseCreated
element.Create
response contains two elements - ResponseCreated
and Shell
.I don't believe there's a clean solution to the problem - it's not possible to mark <part> elements in the <message> as optional.
The workaround in this case is to strip the additional element before it gets to the JAX-WS parser. This can be done by creating a CXF interceptor or a JAX-WS handler and manipulating the response XML.
public class StripHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public boolean handleMessage(SOAPMessageContext context) {
boolean isResponse = Boolean.FALSE.equals(context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY));
if (isResponse) {
QName action = (QName) context.get(SOAPMessageContext.WSDL_OPERATION);
if ("Create".equals(action.getLocalPart())) {
Iterator<?> childIter = getBodyChildren(context);
while(childIter.hasNext()) {
SOAPElement el = (SOAPElement) childIter.next();
if ("Shell".equals(el.getLocalName())) {
childIter.remove();
}
}
}
}
return true;
}
private Iterator<?> getBodyChildren(SOAPMessageContext context) {
try {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
return body.getChildElements();
} catch (SOAPException e) {
throw new IllegalStateException(e);
}
}
@Override
public boolean handleFault(SOAPMessageContext context) {return true;}
@Override
public void close(MessageContext context) {}
@Override
public Set<QName> getHeaders() {return null;}
}
Create handlers.xml
file alongside the service interface:
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-name>StripHandlerr</handler-name>
<handler-class>fully.qualified.StripHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
And attach it to the service definition using the annotation @HandlerChain(file = "handlers.xml")
That's an alternative approach which doesn't require the extra xml file.
((BindingProvider)service).getBinding().setHandlerChain(Arrays.<Handler>asList(new StripHandler());