在 Apache Camel 中使用 CXF 时如何设置 WS-Addressing MessageId header?

How do I set the WS-Addressing MessageId header when using CXF with Apache Camel?

我正在调用需要 WS-Addressing SOAP header 的 Web 服务。我正在使用 Apache Camel 和 CXF 来调用 Web 服务。当我使用 Web 服务的 WSDL 配置 CXF 端点时,它足够智能,可以自动添加 WS-Adressing SOAP headers,但我需要设置自定义 MessageId。

这是当前正在发送的消息:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
    <soap:Header>
        <ws:international xmlns:ws="http://www.w3.org/2005/09/ws-i18n">
            <ws:locale xmlns:ws="http://www.w3.org/2005/09/ws-i18n">en_CA</ws:locale>
        </ws:international>
        <fram:user wsa:IsReferenceParameter="true" xmlns:fram="http://wsbo.webservice.ephs.pdc.ibm.com/Framework/" xmlns:wsa="http://www.w3.org/2005/08/addressing">BESTSystem</fram:user>
        <Action soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">http://webservice.ephs.pdc.ibm.com/Client/QueryHumanSubjects</Action>
        <MessageID soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6</MessageID>
        <To soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">https://panweb5.panorama.gov.bc.ca:8081/ClientWebServicesWeb/ClientProvider</To>
        <ReplyTo soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">
            <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
        </ReplyTo>
    </soap:Header>
    <soap:Body>
        <ns2:queryHumanSubjectsRequest xmlns:ns2="http://wsbo.webservice.ephs.pdc.ibm.com/Client/" xmlns:ns3="http://wsbo.webservice.ephs.pdc.ibm.com/FamilyHealth/">
            <!-- stuff -->
        </ns2:queryHumanSubjectsRequest>
    </soap:Body>
</soap:Envelope>

如您所见,MessageId 值为"urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6"。我需要设置一个自定义值。

我尝试添加 MessageId header,他们以我添加其他 header 的方式添加 "international" 和 "user",但框架的某些部分覆盖了该值。

// Note this doesn't work! Something overrides the value. It works for other headers.
@Override
public void process(Exchange exchange) throws Exception {

    Message in = exchange.getIn();
    List<SoapHeader> headers = CastUtils.cast((List<?>) in.getHeader(Header.HEADER_LIST));

    SOAPFactory sf = SOAPFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
    QName MESSAGE_ID_HEADER = new QName("http://www.w3.org/2005/08/addressing", "MessageID", "wsa");
    SOAPElement messageId = sf.createElement(MESSAGE_ID_HEADER);
    messageId.setTextContent("customValue");
    SoapHeader soapHeader = new SoapHeader(MESSAGE_ID_HEADER, messageId);
    headers.add(soapHeader);
}

The CXF website has some documentation on how to set WS-Addressing headers, but I don't see how to apply it to Apache Camel. The Apache Camel CXF documentation 也没有特别提到 WS-Addressing。

您发布的文档链接确实包含您需要的信息,尽管如何将其应用于 Camel 并不是很明显。

The CXF documentation 表示:

The CXF org.apache.cxf.ws.addressing.impl.AddressingPropertiesImpl object can be used to control many aspects of WS-Addressing including the Reply-To:

AddressingProperties maps = new AddressingPropertiesImpl();
EndpointReferenceType ref = new EndpointReferenceType();
AttributedURIType add = new AttributedURIType();
add.setValue("http://localhost:9090/decoupled_endpoint");
ref.setAddress(add);
maps.setReplyTo(ref);
maps.setFaultTo(ref);
((BindingProvider)port).getRequestContext()
        .put("javax.xml.ws.addressing.context", maps);

请注意,它在 "RequestContext" 上设置寻址属性。

The Apache Camel documentation 表示:

How to propagate a camel-cxf endpoint’s request and response context

CXF client API provides a way to invoke the operation with request and response context. If you are using a camel-cxf endpoint producer to invoke the outside web service, you can set the request context and get response context with the following code:

CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
    public void process(final Exchange exchange) {
        final List<String> params = new ArrayList<String>();
        params.add(TEST_MESSAGE);
        // Set the request context to the inMessage
        Map<String, Object> requestContext = new HashMap<String, Object>();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
        exchange.getIn().setBody(params);
        exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
        exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
    }
});

上面的例子有一些我们不需要的东西,但重要的是它向我们展示了如何设置 CXF 请求上下文。

把它们放在一起你会得到:

@Override
public void process(Exchange exchange) throws Exception {
    AttributedURIType messageIDAttr = new AttributedURIType();
    messageIDAttr.setValue("customValue");

    AddressingProperties maps = new AddressingProperties();
    maps.setMessageID(messageIDAttr);

    Map<String, Object> requestContext = new HashMap<>();
    requestContext.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
    exchange.getIn().setHeader(Client.REQUEST_CONTEXT, requestContext);
}

// org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES = "javax.xml.ws.addressing.context"
// org.apache.cxf.endpoint.Client.REQUEST_CONTEXT = "RequestContext"


警告:在我的路线中,我顺序调用了多个不同的 Web 服务。我发现在如上所示设置 RequestContext 后,Camel 开始对所有 Web 服务使用相同的 RequestContext,这导致错误:"A header representing a Message Addressing Property is not valid and the message cannot be processed"。这是因为不正确的 "Action" header 用于第一次之后的所有 Web 服务调用。

我使用 "RequestContext" Exchange 属性 将其追溯到 Apache Camel,与 header 我们分开set,显然优先于 header。如果我在调用后续 Web 服务之前删除此 属性,CXF 会自动填写正确的操作 header.

如果您的问题没有解决,我建议您将您的cxf 服务与自定义拦截器相结合。使用您的肥皂消息很容易。像这样:

<bean id="TAXWSS4JOutInterceptorBean" name="TAXWSS4JOutInterceptorBean" class="com.javainuse.beans.SetDetailAnswerInterceptor " />
<cxf:cxfEndpoint id="CXFTest" address="/javainuse/learn"
                 endpointName="a:SOATestEndpoint" serviceName="a:SOATestEndpointService"
                 serviceClass="com.javainuse.SOATestEndpoint"
                 xmlns:a ="http://javainuse.com">
    <cxf:binding>
        <soap:soapBinding mtomEnabled="false" version="1.2" />
    </cxf:binding>

    <cxf:features>
        <wsa:addressing  xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
    </cxf:features>
    <cxf:inInterceptors>

        <ref bean="TAXWSS4JInInterceptorBean" />            

    </cxf:inInterceptors>
    <cxf:inFaultInterceptors>

    </cxf:inFaultInterceptors>
    <cxf:outInterceptors>
        <ref bean="TAXWSS4JOutInterceptorBean" />
    </cxf:outInterceptors>
    <cxf:outFaultInterceptors>

    </cxf:outFaultInterceptors>
</cxf:cxfEndpoint>

在拦截器中,您可以像这样设置 soap headers:

public class SetDetailAnswerInterceptor extends WSS4JOutInterceptor {

public SetDetailAnswerInterceptor() {

}

@Override
public void handleMessage(SoapMessage mc) {
    AttributedURIType value = new AttributedURIType();
    value.setValue("test");
    ((AddressingProperties) mc.get("javax.xml.ws.addressing.context.outbound")).setMessageID(value);
}
}