1
votes

There is a lot of information about Apache Camel + CXF-Endpoint and RPC/encoded legacy webservices. But until now I did not find a solution for the problem.

I want to call an RPC/encoded webservice from Apache Camel over the CXF endpoint. CXF does not support RPC/encoded Webservices. So I tried two approaches to solve the problem.

  1. Convert wsdl from RPC/encoded to RPC/literal and generate source files. Call the webservice in RPC/literal style which is supported by CXF. The following article suggests that this approach could be a solution for my problem: Best way to consume RPC/encoded webservice?

  2. Send the complete SOAP-Message without mapping to objects (no JAXB).

Neither approach 1 nor approach 2 works. In the following sections I will explain my approaches and the problems in more detail.

Prerequisites

First approach: Convert wsdl RPC/encoded to RPC/literal and generate sources

In the RCP/encoded wsdl I have changed following:

WSDL Bindings:

<wsdl:binding name="exampleSoapBinding" type="impl:MyFunctionalWebservices">
   <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
   <wsdl:operation name="isAlive">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="isAliveRequest">
         <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://my.example.com/myFunction" use="encoded"/>
      </wsdl:input>
...

to

<wsdl:binding name="exampleSoapBinding" type="impl:MyFunctionalWebservices">
   <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
   <wsdl:operation name="isAlive">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="isAliveRequest">
         <wsdlsoap:body  namespace="http://my.example.com/myFunction" use="literal"/>
      </wsdl:input>
…

Arrays of Objects:

<complexType name="ArrayOfMyElement">
 <complexContent>
  <restriction base="soapenc:Array">
   <attribute ref="soapenc:arrayType" wsdl:arrayType="impl:MyElement[]"/>
  </restriction>
 </complexContent>
</complexType>

to

<complexType name="ArrayOfMyElement">
    <xsd:sequence>
        <xsd:element name="MyElement"
                     type="impl:MyElement"
                     minOccurs="0"
                     maxOccurs="unbounded"/>
    </xsd:sequence>
</complexType>

Arrays of simple types:

<complexType name="ArrayOf_xsd_string">
 <complexContent>
  <restriction base="soapenc:Array">
   <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
  </restriction>
 </complexContent>
</complexType>

to

<complexType name="ArrayOf_xsd_string">
    <xsd:sequence>
        <xsd:element name="item"
                     type="xsd:string"
                     minOccurs="0"
                     maxOccurs="unbounded"/>
    </xsd:sequence>
</complexType>

Arrays of undefined Type (anyType):

<complexType name="ArrayOf_xsd_anyType">
 <complexContent>
  <restriction base="soapenc:Array">
   <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:anyType[]"/>
  </restriction>
 </complexContent>
</complexType>

to

<complexType name="ArrayOf_xsd_anyType">
    <xsd:sequence>
        <xsd:element name="item" 
                     type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
</complexType>

After that I have generated source files with IntelliJ Webservice plugin (over CXF wsdl2java)

In Camel I have configured following Endpoint:

    CxfEndpoint endpoint = new CxfEndpoint();
    endpoint.setAddress("http://127.0.0.1:9000/myfunctionalmock");
    endpoint.setWsdlURL("wsdl/myservice_literal.wsdl");
    endpoint.setServiceClass("com.my.example.MyFunctionalWebservices");
    endpoint.setEndpointNameString("{http://my.example.com/myFunction}rpcrouter");
    endpoint.setServiceNameString("{http://my.example.com/myFunction}MyFunctionalWebservicesService");
    endpoint.setDataFormat(DataFormat.POJO);
    endpoint.setSynchronous(true);
    endpoint.setCamelContext(camelContext);
    endpoint.setEndpointUriIfNotSpecified(MY_ENDPOINT_URL);
    camelContext.addEndpoint(MY_ENDPOINT_URL, endpoint);

Usage of CXF-Endpoint in Camel route:

I want to call following function of webservice:

public Result isAlive(java.lang.String identifier);

The timer in the camel route is only for triggering the webservice.

    from("timer://myTimer?period=10000")
    .log(LoggingLevel.INFO, "START Timer Webservice.")
    .setBody().constant("1620000018")
    .setHeader("operationName", constant("isAlive"))
    .setHeader("operationNamespace", constant("http://my.example.com/myFunction"))
    .to(MyCamelConfiguration.MY_ENDPOINT_URL);

Problems with this approach:

At runtime following message appears at deployment time:

2015-03-05 09:57:46,659; 2010; [localhost-startStop-1]; DEBUG;                wsdl11.WSDLServiceBuilder; Operation {http://my.example.com/myFunction}isAlive cannot be unwrapped, input message must reference global element declaration with same localname as operation

Following exception occurs at runtime:

org.apache.cxf.binding.soap.SoapFault: No namespace on "HTML" element. You must send a SOAP request.
        at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.readVersion(ReadHeadersInterceptor.java:111)
        at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:155)
        at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:62)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
        at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:835)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1614)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1504)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1310)
        at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
        at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:628)
        at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
        at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:565)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:474)
        at org.apache.camel.component.cxf.CxfProducer.process(CxfProducer.java:149)
        at org.apache.camel.impl.SynchronousDelegateProducer.process(SynchronousDelegateProducer.java:62)
        at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
        at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:120)
        at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:72)
        at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:416)
        at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
        at org.apache.camel.processor.Pipeline.process(Pipeline.java:118)
        at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
        at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
        at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:166)
        at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:74)
        at java.util.TimerThread.mainLoop(Timer.java:512)
        at java.util.TimerThread.run(Timer.java:462)

Second approach: Send the SOAP-Message without mapping to Objects.

Endpoint definition in Camel:

CxfEndpoint endpoint = new CxfEndpoint();
        endpoint.setAddress("http://127.0.0.1:9000/myfunctionalmock");
        endpoint.setEndpointNameString("{http://my.example.com/myFunction}rpcrouter");
        endpoint.setServiceNameString("{http://my.example.com/myFunction}MyFunctionalWebservicesService");
        endpoint.setDataFormat(DataFormat.RAW);
        endpoint.setWrappedStyle(false);
        endpoint.setSynchronous(true);
        endpoint.setCamelContext(camelContext);
        endpoint.setEndpointUriIfNotSpecified(MY_TEMPLATE_ENDPOINT_URL);
        camelContext.addEndpoint(MY_TEMPLATE_ENDPOINT_URL, endpoint);

Usage in route:

        from("timer://myTimer?period=10000")
        .log(LoggingLevel.INFO, "START Timer Webservice.")
        .setBody().constant(
                "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:myns=\"http://my.example.com/myFunction\">\n" +
                        "   <soapenv:Header/>\n" +
                        "   <soapenv:Body>\n" +
                        "      <myns:isAlive soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" +
                        "         <identifier xsi:type=\"xsd:string\">1620000018</identifier>\n" +
                        "      </myns:isAlive>\n" +
                        "   </soapenv:Body>\n" +
                        "</soapenv:Envelope>"
        )
        .to(MyCamelConfiguration.MY_TEMPLATE_ENDPOINT_URL)
.log(LoggingLevel.INFO, "END Timer Webservice.")
.log(LoggingLevel.INFO, "Body after ws call = ${body}");

But the webservice on http://localhost:9000/myfunctionalmock is never called. I found following log messages in logfile:

2015-03-05 10:56:35,522; 12843; [Camel (camel-1) thread #0 - timer://myTimer]; DEBUG;              phase.PhaseInterceptorChain; Invoking handleMessage on interceptor org.apache.cxf.jaxb.attachment.JAXBAttachmentSchemaValidationHack@1d3694a
2015-03-05 10:56:35,523; 12844; [Camel (camel-1) thread #0 - timer://myTimer]; DEBUG;              phase.PhaseInterceptorChain; Invoking handleMessage on interceptor org.apache.cxf.ws.policy.PolicyVerificationInInterceptor@1a0ff10
2015-03-05 10:56:35,523; 12844; [Camel (camel-1) thread #0 - timer://myTimer]; DEBUG;   policy.PolicyVerificationInInterceptor; Verified policies for inbound message.
2015-03-05 10:56:35,523; 12844; [Camel (camel-1) thread #0 - timer://myTimer]; INFO ;               helpers.MarkerIgnoringBase; END Timer Webservice.
2015-03-05 10:56:35,523; 12844; [Camel (camel-1) thread #0 - timer://myTimer]; INFO ;               helpers.MarkerIgnoringBase; Body after ws call = <HTML>
<HEAD><TITLE>Redirection</TITLE></HEAD>
<BODY><H1>Redirect</H1></BODY>

Both approaches do not work.Is there a possibility to call RPC/encoded webservice over CXF in Camel?

Thanks in advance.

Regards,

Max

1
So in this case another solution is to use Axis which does support RPC services. Camel has no Axis component but you could use the Axis library from within a processor/bean.Namphibian

1 Answers

2
votes

As you say Apache CXF does not support the old RPC style. You would need to use an older WS library such as Apache Axis 1.x. There is no Camel component for that, but as its all just java code, you can write some java code that uses Axis 1.x, and then let Camel call the java code, using its bean component / processor.

Another alternative is that as SOAP is over HTTP you can just use Camel's HTTP components also. But you would need to build the message body and headers according to the RPC style, but that should not be so hard either to do.