How do I specify the WS-Addressing version with a JAX-WS client?

Foran picture Foran · Jun 9, 2014 · Viewed 8.5k times · Source

I generated a SOAP 1.2 web service client with wsimport (JDK 1.7). I need it to explicitly use WS-Addressing 2004/08 and not 2005/08. The closest I could find for instanciating the client was

import MyService.*;
import javax.xml.ws.BindingProvider;

public class test {

    public static void main(String[] args) {
        MyService service = new MyService();  
        IMyService proxy = service.getMyService(new javax.xml.ws.soap.AddressingFeature(true, true) );  
        ((BindingProvider)proxy).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://192.168.0.5:1234/services/MyService");
        proxy.Ping("Foo");
      }
}

The important bit being

MyService service = new MyService();
IMyService proxy = service.getMyService(new javax.xml.ws.soap.AddressingFeature(true, true));

Unfortunately, this results in 2005/08 addressing. Not supplying an argument to getMyService() results in not using WS-Addressing.

The only examples I can find on Google that force 2004/08 Addressing use Axis2 (the whole reason I want JAX-WS is to move away from Axis2)

The difference on the wire is (2004/08)

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
    <s:Header>
        <a:Action s:mustUnderstand="1">http://www.example.com/schemas/service/myservice/IMyService/Ping</a:Action>
        <a:MessageID>urn:uuid:87727401-b1a0-4667-9ef0-c64e58800ff6</a:MessageID>
        <a:ReplyTo>
            <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
        </a:ReplyTo>
        <a:To s:mustUnderstand="1">https://192.168.0.5:1234/services/MyService</a:To>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <Ping xmlns="http://www.example.com/schemas/service/myservice">
            <Message>Foo</Message>
        </Ping>
    </s:Body>
</s:Envelope>

(2005/08)

<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
    <S:Header>
        <To xmlns="http://www.w3.org/2005/08/addressing">https://192.168.0.5:1234/services/MyService</To>
        <Action xmlns="http://www.w3.org/2005/08/addressing" xmlns:S="http://www.w3.org/2003/05/soap-envelope" S:mustUnderstand="true">http://www.example.com/schemas/service/myservice/IMyService/Ping</Action>
        <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
            <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
        </ReplyTo>
        <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:87727401-b1a0-4667-9ef0-c64e58800ff6</MessageID>
    </S:Header>
    <S:Body>
        <Ping xmlns="http://www.example.com/schemas/service/myservice">
            <Message>Foo</Message>
        </Ping>
    </S:Body>
</S:Envelope>

Anyone have any ideas here?

Answer

Foran picture Foran · Jun 13, 2014

I found a way to do this, but I'm not convinced that it's the best solution.

It involves creating a custom SOAPHandler and manually injecting the Addressing headers and stripping out the HTTP header soapaction

public static class Addressing2004SoapHandler implements SOAPHandler<SOAPMessageContext>
{
    private String mEndpoint;

    public Addressing2004SoapHandler(String endpoint) {
        super();
        mEndpoint = endpoint;
    }

    public Set<QName> getHeaders()
    {
        Set<QName> retval = new HashSet<QName>();
        retval.add(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "Action"));
        retval.add(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "MessageID"));
        retval.add(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "ReplyTo"));
        retval.add(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "To"));
        return retval;
    }

    public boolean handleMessage(SOAPMessageContext messageContext)
    {
        Boolean outboundProperty = (Boolean)messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if (outboundProperty.booleanValue()) {
            try {
                messageContext.put(MessageContext.HTTP_REQUEST_HEADERS, Collections.singletonMap("Content-Type",Collections.singletonList("application/soap+xml; charset=utf-8")));
                SOAPMessage message = messageContext.getMessage();
                if(message.getSOAPPart().getEnvelope().getHeader() == null) {
                        message.getSOAPPart().getEnvelope().addHeader();
                }
                SOAPHeader header = message.getSOAPPart().getEnvelope().getHeader();
                SOAPHeaderElement actionElement = header.addHeaderElement(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "Action"));
                actionElement.setMustUnderstand(true);
                String action = (String)messageContext.get("javax.xml.ws.soap.http.soapaction.uri");
                messageContext.put("javax.xml.ws.soap.http.soapaction.uri", null);
                actionElement.addTextNode(action);
                header.addHeaderElement(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "MessageID")).addTextNode("uuid:" + UUID.randomUUID().toString());
                SOAPHeaderElement replyToElement = header.addHeaderElement(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "ReplyTo"));
                SOAPElement addressElement = replyToElement.addChildElement(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "Address"));
                addressElement.addTextNode("http://www.w3.org/2004/08/addressing/anonymous");
                SOAPHeaderElement toElement = header.addHeaderElement(new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing", "To"));
                toElement.setMustUnderstand(true);
                String endpoint = (String)messageContext.get("javax.xml.ws.service.endpoint.address");
                toElement.addTextNode(endpoint);
            }
            catch(SOAPException ex) {
            }
        }

        return true;
    }

    public boolean handleFault(SOAPMessageContext messageContext)
    {
            return true;
    }
    public void close(MessageContext messageContext)
    {
    }
}

Here is the updated test Class

public class test {
    public static void main(String[] args) {
        MyService service = new MyService();  
        IMyService proxy = service.getMyService();  
        javax.xml.ws.Binding binding = ((BindingProvider)proxy).getBinding();
        List<Handler> handlerList = binding.getHandlerChain();
        handlerList.add(new Addressing2004SoapHandler(endpoint));
        binding.setHandlerChain(handlerList);
        Map<String, Object> requestContext = ((BindingProvider)proxy).getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://192.168.0.5:1234/services/MyService");
        proxy.Ping("Foo");
    }
}

There is a side effect, I've not yet figured out how to solve, this results in a <?xml version="1.0"?> xml declaration which conflicts with the HTTP Content-type. I'll post an update when I've figured that out.