使用Apache Camel和CXF Endpoint调用RPC / encoded Web服务

时间:2015-03-05 11:00:26

标签: web-services soap cxf apache-camel soap-rpc-encoded

有很多关于Apache Camel + CXF-Endpoint和RPC /编码遗留Web服务的信息。 但直到现在我还没有找到问题的解决方案。

我想通过CXF端点从Apache Camel调用RPC /编码的Web服务。 CXF不支持RPC /编码的Web服务。 所以我尝试了两种方法来解决问题。

  1. 将wsdl从RPC / encoded转换为RPC / literal并生成源文件。 以CXF支持的RPC / literal样式调用Web服务。 以下文章表明,这种方法可以解决我的问题:Best way to consume RPC/encoded webservice?

  2. 发送完整的SOAP-Message而不映射到对象(无JAXB)。

  3. 方法1和方法2都不起作用。 在下面的章节中,我将更详细地解释我的方法和问题。

    的先决条件

    第一种方法:将wsdl RPC / encoded转换为RPC / literal并生成源

    在RCP / encoded wsdl中,我改变了以下内容:

    WSDL绑定:

    <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>
    ...
    

    <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>
    …
    

    物体阵列:

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

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

    简单类型的数组:

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

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

    未定义类型(anyType)的数组:

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

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

    之后我用IntelliJ Webservice插件生成源文件(通过CXF wsdl2java)

    在Camel中,我配置了以下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);
    

    在Camel路由中使用CXF-Endpoint:

    我想调用webservice的以下功能:

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

    驼峰路由中的计时器仅用于触发Web服务。

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

    这种方法存在问题:

    在运行时,在部署时出现以下消息:

    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
    

    在运行时发生以下异常:

    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)
    

    第二种方法:发送SOAP-Message而不映射到Objects。

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

    路线使用:

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

    但永远不会调用http://localhost:9000/myfunctionalmock上的网络服务。 我在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>
    

    这两种方法都不起作用。是否有可能在Camel中通过CXF调用RPC / encoded webservice?

    提前致谢。

    此致

    最高

1 个答案:

答案 0 :(得分:2)

正如您所说,Apache CXF 支持旧的RPC样式。您需要使用较旧的WS库,例如Apache Axis 1.x.没有Camel组件,但是因为它只是java代码,你可以编写一些使用Axis 1.x的java代码,然后让Camel使用它的bean组件/处理器调用java代码。

另一种选择是,当SOAP通过HTTP时,您也可以使用Camel的HTTP组件。但是你需要根据RPC样式构建消息体和头文件,但这不应该那么难。