如何使用 JAX-WS 客户端指定 WS-Addressing 版本?

Posted

技术标签:

【中文标题】如何使用 JAX-WS 客户端指定 WS-Addressing 版本?【英文标题】:How do I specify the WS-Addressing version with a JAX-WS client? 【发布时间】:2014-07-30 10:15:31 【问题描述】:

我使用 wsimport (JDK 1.7) 生成了一个 SOAP 1.2 Web 服务客户端。我需要它来明确使用 WS-Addressing 2004/08 而不是 2005/08。 我能找到的最接近的实例化客户端是

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");
      

重要的一点

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

不幸的是,这导致了 2005/08 寻址。不向 getMyService() 提供参数会导致不使用 WS-Addressing。

我可以在 Google 上找到的唯一示例强制 2004/08 寻址使用 Axis2(我想要 JAX-WS 的全部原因是远离 Axis2)

电线上的区别是 (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>

这里有人有什么想法吗?

【问题讨论】:

我想知道...您是否应该在应用程序级别上做这件事,或者在 ESB(例如 DataPower)级别上做这件事并让您的应用程序假定它接收到的数据是干净的(只是为了卸载工作)。 【参考方案1】:

我找到了一种方法来做到这一点,但我不相信这是最好的解决方案。

它涉及创建自定义 SOAPHandler 并手动注入 Addressing 标头并剥离 HTTP 标头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)
    
    

这是更新的测试类

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");
    

有一个副作用,我还没有想出如何解决,这会导致 xml 声明与 HTTP 内容类型冲突。等我弄清楚了,我会发布更新。

【讨论】:

【参考方案2】:

我使用了来自 jaxws-rt 的 MemberSubmissionAddressingFeature

IMyService proxy = service.getMyService(new com.sun.xml.ws.developer.MemberSubmissionAddressingFeature(true, true) );

这是maven http://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt/2.2.7的pom文件依赖

【讨论】:

以上是关于如何使用 JAX-WS 客户端指定 WS-Addressing 版本?的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式设置 JAX-WS 客户端的 SSLContext?

如何使用 Java JAX-WS 添加 SOAP 标头

如何为 JAX-WS Web 服务客户端设置超时?

“便携式”JAX-WS 客户端

如何避免需要在 CXF 或 JAX-WS 生成的 Web 服务客户端中指定 WSDL 位置?

如何在 GlassFish 上从 JAX-WS 向 Swing 客户端构建推送通知?