Вызовите RPC/ закодированный веб-сервис с Apache Camel и CXF Endpoint
Существует много информации об устаревших веб-сервисах Apache Camel + CXF-Endpoint и RPC/ закодированных. Но до сих пор я не нашел решения проблемы.
Я хочу вызвать RPC/ закодированный веб-сервис от Apache Camel через конечную точку CXF. CXF не поддерживает RPC/ закодированные веб-сервисы. Поэтому я попробовал два подхода к решению проблемы.
Конвертируйте wsdl из RPC/ закодированный в RPC/literal и генерируйте исходные файлы. Вызовите веб-сервис в стиле RPC/literal, который поддерживается CXF. Следующая статья предполагает, что этот подход мог бы решить мою проблему: Лучший способ использовать RPC/ кодированный веб-сервис?
Отправьте полное 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, но это не должно быть так сложно сделать.