使用 Camel、CXF 和 ProxyBuilder/ProxyHelper 调用 SOAP-WS
Calling SOAP-WS with Camel, CXF and ProxyBuilder/ProxyHelper
我正在尝试使用 apache camel(版本 2.14.1)和 cxf(版本 3.0.3)进行简单的 (SOAP) 网络服务调用。
我想调用的远程方法需要一个浮点数和 returns 一个浮点数:
public float getVolume(float vol) {
float f = vol * vol;
return f;
}
我在 ProducerTemplate 的帮助下设法调用了它。没问题。
现在我想像调用对象的方法一样调用它。为此,我使用了这样的 ProxyBuilder
:
TestService service = new ProxyBuilder(context).endpoint(endpoint).build(TestService.class);
作为 ProxyBuilder
的替代方案,可以使用 ProxyHelper
,这没有区别。
我的路线:
String cxfUri = "cxf:http://localhost/9202/testService?serviceClass=" + TestService.class.getName();
from("direct:start").log("${body}").process(new Processor() {
@Override
public void process(Exchange e) throws Exception {
final BeanInvocation bi = e.getIn().getBody(BeanInvocation.class);
e.getIn().setBody(bi.getArgs());
}
}).to(cxfUri);
(从这里得到提示:Camel: Bean Proxy to CXF Endpoint)
如果我这样调用方法:
System.out.println("Volume: " + service.getVolume(42f));
出现以下异常:
org.apache.camel.InvalidPayloadException: No body available of type: float but has value: [1764.0] of type: org.apache.cxf.message.MessageContentsList on: Message: [1764.0]. Caused by: No type converter available to convert from type: org.apache.cxf.message.MessageContentsList to the required type: float with value [1764.0].
如果我的方法 returns a String 一切正常。
我读到 BeanInvocation 中的值必须是可序列化的。
这是问题所在(返回原始类型)吗?
此外,如果服务提供两种方法(第一种返回字符串,第二种返回浮点数),它会尝试将浮点数转换为字符串。
我尝试了不同的 cxf 版本 (2.7.14)、不同的路由并在路由定义中使用选项,例如定义 WSDL 位置、将模式设置为有效负载、定义默认操作。
我也在网上搜索了几个小时,但没有找到有效的提示。
谁能告诉我我做错了什么?
编辑:
通过向路由添加第二个处理器解决了第一个问题:
from("direct:start").process(new Processor() {
@Override
public void process(Exchange e) throws Exception {
BeanInvocation bi = e.getIn().getBody(BeanInvocation.class);
e.getIn().setBody(bi.getArgs());
}
}).to(cxfUri).process(new Processor() {
@Override
public void process(Exchange e) throws Exception {
MessageContentsList list = e.getIn().getBody(MessageContentsList.class);
if (list.size() > 0) {
e.getIn().setBody(list.get(0));
}
}
});
这没有解决第二个问题:
Also, If the service offers two methods (first one returning a String, second one returning a float) it tries to convert the float to a String.
发生以下异常:
Exception in thread "main" org.apache.cxf.interceptor.Fault: java.lang.Float cannot be cast to java.lang.String
at org.apache.cxf.jaxws.interceptors.WrapperClassOutInterceptor.handleMessage(WrapperClassOutInterceptor.java:117)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:516)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:418)
at org.apache.camel.component.cxf.CxfProducer.process(CxfProducer.java:112)
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.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:105)
at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:87)
at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:40)
at org.apache.camel.component.bean.AbstractCamelInvocationHandler.call(AbstractCamelInvocationHandler.java:110)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at org.apache.camel.component.bean.AbstractCamelInvocationHandler.invokeWithBody(AbstractCamelInvocationHandler.java:128)
at org.apache.camel.component.bean.CamelInvocationHandler.doInvokeProxy(CamelInvocationHandler.java:45)
at org.apache.camel.component.bean.AbstractCamelInvocationHandler.invoke(AbstractCamelInvocationHandler.java:82)
at com.sun.proxy.$Proxy6.getVolume(Unknown Source)
at soapTest.client.TestServiceClientCXF.main(TestServiceClientCXF.java:49)
Caused by: java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.String
at soapTest.service.GetName_WrapperTypeHelper1.createWrapperObject(Unknown Source)
at org.apache.cxf.jaxws.interceptors.WrapperClassOutInterceptor.handleMessage(WrapperClassOutInterceptor.java:101)
... 21 more
终于找到答案了
错误是使用 org.apache.camel.builder.ProxyBuilder
.
它与 cxf 不完全兼容。
相反,必须使用 org.apache.cxf.common.util.ProxyHelper
和自己的调用处理程序:
final ProducerTemplate template = context.createProducerTemplate();
final Endpoint endpoint = context.getEndpoint("direct:start");
ClassLoader classLoader = context.getApplicationContextClassLoader();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return template.requestBodyAndHeader(endpoint, args, "operationName", method.getName(), method.getReturnType());
}
};
TestService service = (TestService) ProxyHelper.getProxy(classLoader, new Class[] {TestService.class}, invocationHandler);
路由也需要一个处理器(不返回简单类型是行不通的):
from("direct:start").to(cxfUri).process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
MessageContentsList list = exchange.getIn().getBody(MessageContentsList.class);
if (list.size() > 0) {
exchange.getIn().setBody(list.get(0));
}
}
});
我正在尝试使用 apache camel(版本 2.14.1)和 cxf(版本 3.0.3)进行简单的 (SOAP) 网络服务调用。
我想调用的远程方法需要一个浮点数和 returns 一个浮点数:
public float getVolume(float vol) {
float f = vol * vol;
return f;
}
我在 ProducerTemplate 的帮助下设法调用了它。没问题。
现在我想像调用对象的方法一样调用它。为此,我使用了这样的 ProxyBuilder
:
TestService service = new ProxyBuilder(context).endpoint(endpoint).build(TestService.class);
作为 ProxyBuilder
的替代方案,可以使用 ProxyHelper
,这没有区别。
我的路线:
String cxfUri = "cxf:http://localhost/9202/testService?serviceClass=" + TestService.class.getName();
from("direct:start").log("${body}").process(new Processor() {
@Override
public void process(Exchange e) throws Exception {
final BeanInvocation bi = e.getIn().getBody(BeanInvocation.class);
e.getIn().setBody(bi.getArgs());
}
}).to(cxfUri);
(从这里得到提示:Camel: Bean Proxy to CXF Endpoint)
如果我这样调用方法:
System.out.println("Volume: " + service.getVolume(42f));
出现以下异常:
org.apache.camel.InvalidPayloadException: No body available of type: float but has value: [1764.0] of type: org.apache.cxf.message.MessageContentsList on: Message: [1764.0]. Caused by: No type converter available to convert from type: org.apache.cxf.message.MessageContentsList to the required type: float with value [1764.0].
如果我的方法 returns a String 一切正常。
我读到 BeanInvocation 中的值必须是可序列化的。
这是问题所在(返回原始类型)吗?
此外,如果服务提供两种方法(第一种返回字符串,第二种返回浮点数),它会尝试将浮点数转换为字符串。
我尝试了不同的 cxf 版本 (2.7.14)、不同的路由并在路由定义中使用选项,例如定义 WSDL 位置、将模式设置为有效负载、定义默认操作。
我也在网上搜索了几个小时,但没有找到有效的提示。
谁能告诉我我做错了什么?
编辑:
通过向路由添加第二个处理器解决了第一个问题:
from("direct:start").process(new Processor() {
@Override
public void process(Exchange e) throws Exception {
BeanInvocation bi = e.getIn().getBody(BeanInvocation.class);
e.getIn().setBody(bi.getArgs());
}
}).to(cxfUri).process(new Processor() {
@Override
public void process(Exchange e) throws Exception {
MessageContentsList list = e.getIn().getBody(MessageContentsList.class);
if (list.size() > 0) {
e.getIn().setBody(list.get(0));
}
}
});
这没有解决第二个问题:
Also, If the service offers two methods (first one returning a String, second one returning a float) it tries to convert the float to a String.
发生以下异常:
Exception in thread "main" org.apache.cxf.interceptor.Fault: java.lang.Float cannot be cast to java.lang.String at org.apache.cxf.jaxws.interceptors.WrapperClassOutInterceptor.handleMessage(WrapperClassOutInterceptor.java:117) at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307) at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:516) at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:418) at org.apache.camel.component.cxf.CxfProducer.process(CxfProducer.java:112) 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.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:105) at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:87) at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:40) at org.apache.camel.component.bean.AbstractCamelInvocationHandler.call(AbstractCamelInvocationHandler.java:110) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at org.apache.camel.component.bean.AbstractCamelInvocationHandler.invokeWithBody(AbstractCamelInvocationHandler.java:128) at org.apache.camel.component.bean.CamelInvocationHandler.doInvokeProxy(CamelInvocationHandler.java:45) at org.apache.camel.component.bean.AbstractCamelInvocationHandler.invoke(AbstractCamelInvocationHandler.java:82) at com.sun.proxy.$Proxy6.getVolume(Unknown Source) at soapTest.client.TestServiceClientCXF.main(TestServiceClientCXF.java:49) Caused by: java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.String at soapTest.service.GetName_WrapperTypeHelper1.createWrapperObject(Unknown Source) at org.apache.cxf.jaxws.interceptors.WrapperClassOutInterceptor.handleMessage(WrapperClassOutInterceptor.java:101) ... 21 more
终于找到答案了
错误是使用 org.apache.camel.builder.ProxyBuilder
.
它与 cxf 不完全兼容。
相反,必须使用 org.apache.cxf.common.util.ProxyHelper
和自己的调用处理程序:
final ProducerTemplate template = context.createProducerTemplate();
final Endpoint endpoint = context.getEndpoint("direct:start");
ClassLoader classLoader = context.getApplicationContextClassLoader();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return template.requestBodyAndHeader(endpoint, args, "operationName", method.getName(), method.getReturnType());
}
};
TestService service = (TestService) ProxyHelper.getProxy(classLoader, new Class[] {TestService.class}, invocationHandler);
路由也需要一个处理器(不返回简单类型是行不通的):
from("direct:start").to(cxfUri).process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
MessageContentsList list = exchange.getIn().getBody(MessageContentsList.class);
if (list.size() > 0) {
exchange.getIn().setBody(list.get(0));
}
}
});