Вызовите RPC/ закодированный веб-сервис с Apache Camel и CXF Endpoint

Существует много информации об устаревших веб-сервисах Apache Camel + CXF-Endpoint и RPC/ закодированных. Но до сих пор я не нашел решения проблемы.

Я хочу вызвать RPC/ закодированный веб-сервис от Apache Camel через конечную точку CXF. CXF не поддерживает RPC/ закодированные веб-сервисы. Поэтому я попробовал два подхода к решению проблемы.

  1. Конвертируйте wsdl из RPC/ закодированный в RPC/literal и генерируйте исходные файлы. Вызовите веб-сервис в стиле RPC/literal, который поддерживается CXF. Следующая статья предполагает, что этот подход мог бы решить мою проблему: Лучший способ использовать RPC/ кодированный веб-сервис?

  2. Отправьте полное SOAP-сообщение без сопоставления с объектами (без JAXB).

Ни подход 1, ни подход 2 не работают. В следующих разделах я объясню мои подходы и проблемы более подробно.

Предпосылки

  • Apache Tomcat 7
  • Apache Camel 2.14.1
  • Apache CXF 2.7.10
  • Конечная точка веб-сервиса проверяется с помощью SOAP-UI 5.0.0 на http://localhost:9000/myfunctionalmock

Первый подход: конвертировать wsdl RPC/ закодированный в RPC/literal и генерировать источники

В RCP/ закодированный 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 я настроил следующую конечную точку:

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

Использование CXF-Endpoint на маршруте Camel:

Я хочу вызвать следующую функцию веб-сервиса:

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

Таймер на верблюжьем маршруте предназначен только для запуска веб-сервиса.

    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-сообщение без сопоставления с объектами.

Определение конечной точки в 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 никогда не вызывается. Я нашел следующие сообщения журнала в файле журнала:

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>

Оба подхода не работают. Есть ли возможность вызвать RPC/ кодированный веб-сервис через CXF в Camel?

Заранее спасибо.

С Уважением,

Максимум

1 ответ

Решение

Как вы говорите, Apache CXF не поддерживает старый стиль RPC. Вам нужно будет использовать более старую библиотеку WS, такую ​​как Apache Axis 1.x. Для этого не существует компонента Camel, но, поскольку все это просто код Java, вы можете написать некоторый код Java, который использует Axis 1.x, а затем позволить Camel вызывать код Java, используя компонент / процессор бобов.

Другой альтернативой является то, что, поскольку SOAP работает по протоколу HTTP, вы также можете просто использовать HTTP-компоненты Camel. Но вам нужно будет построить тело сообщения и заголовки в соответствии со стилем RPC, но это не должно быть так сложно сделать.

Другие вопросы по тегам