Search code examples
apache-camelwsdlcxfsoap-clientcamel-cxf

Apache Camel CXF difficulty calling an RPC/Encoded WSDL when updating list of elements


While not officially supported, with a few minor modifications to the WSDL I was able to successfully generate CXF Objects for the WSDL and get Camel CXF to talk to an RPC/Encoded WSDL endpoint. The code is incredibly simple and most request/responses work without issue except for attempting to send updates of a list of elements. Here is what the service expects:

<elements arrayType="UpdateElement">

VS here is what is being sent:

<elements>

I need to add the arrayType into the outgoing message. I looked into a number of ways of doing this:

1) An interceptor right before the SOAP message is sent by CXF then use XPath to add the element but I was not clear how to accomplish this using Apache Camel + Camel CXF. How to retrieve the CXF client from the Camel Context?

MyService client = ???

2) Fix it via WSDL? Is it possible to add this element to the WSDL so it is generated as a part of the CXF Objects? It is defined like this presently:

<message name="wsdlElementRequest"> <part name="elements" type="tns:UpdateElements" /></message>

'message' and 'part' come from http://schemas.xmlsoap.org/wsdl/.

Any ideas or suggestions would be appreciated. Thanks!


Solution

  • In case anyone ever stumbles on this with a similar issue, I figured it out myself. I was able to retrieve the CxfEndpoint via CamelContext:

    camelContext.getEndpoint(endpointUrl, CxfEndpoint.class);
    

    Then I was able to add the interceptor I created:

    public class MyCxfInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    ...
    

    Using the CxfEndpoint methods:

    cxfEndpoint.getOutInterceptors().add(new MyCxfInterceptor());
    

    In my interceptor I also incorporated another interceptor, SAAJOutInterceptor, that converts the SOAP into an easy to work with object:

    private List<PhaseInterceptor<? extends Message>> extras = new ArrayList<>(1);
    
    public MyCxfInterceptor() {
        super(Phase.USER_PROTOCOL);
        extras.add(new SAAJOutInterceptor());
    }
    
    public Collection<PhaseInterceptor<? extends Message>> getAdditionalInterceptors() {
        return extras;
    }
    

    The easy to work with SOAP message:

    @Override
    public void handleMessage(SoapMessage soapMessage) throws Fault {
        SOAPMessage msg = soapMessage.getContent(SOAPMessage.class);
    
        try {
            SOAPBody soapBody = msg.getSOAPBody();
    

    Then it was a simple matter of using XPATH to make the correction to the outgoing SOAP message.

    private XPath xpath = XPathFactory.newInstance().newXPath();
    ...
    NodeList nodeList = soapBody.getElementsByTagName("tagName");
    for (int x = 0; x < nodeList.getLength(); x++) {
        Node node = nodeList.item(x);
        ((Element) node).setAttribute("missingAttributeName", "missingAttributeValue");
    }
    

    I hope this helps anyone working with challenging SOAP services!

    Credit to the blog which played a big part in enabling me to implement this solution: https://xceptionale.wordpress.com/2016/06/26/message-interceptor-to-modify-outbound-soap-request/