简体   繁体   中英

Calling SOAP-WS with Camel, CXF and ProxyBuilder/ProxyHelper

I'm trying to get a simple (SOAP) webservice-call working with apache camel (version 2.14.1) and cxf (version 3.0.3).

The remote method i want to call takes a float and returns a float:

public float getVolume(float vol) {
    float f = vol * vol;
    return f;
}

I managed to call it with the help of a ProducerTemplate. That's working fine.
Now I want to call it like the method of an Object. For that I'm using a ProxyBuilder like this:

TestService service = new ProxyBuilder(context).endpoint(endpoint).build(TestService.class);  

As alternative to ProxyBuilder a ProxyHelper can be used, that makes no difference.
My Route:

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

(Got the hint from here: Camel: Bean Proxy to CXF Endpoint )
If I'm calling the method like this:

System.out.println("Volume: " + service.getVolume(42f));

The following Exception appears:

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

If my method returns a String all works fine.
I read that the values in BeanInvocation must be serialisable.
Is this the problem (returning a primitive type)?
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.
I tried a different cxf version (2.7.14), different routes and using options in route definition like defining WSDL location, setting mode to Payload, defining default operation.

I also searched some hours in the Internet, but didn't find a working hint.

Can someone tell me what I'm doing wrong?

Edit:
Solved the first Problem by adding a second processor to the route:

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

This did not solve the second problem:

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.

The following exception occures:

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.proces s(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$1.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(TestServiceClie ntCXF.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

Finally, I found the answer.
The mistake was using org.apache.camel.builder.ProxyBuilder .
It is not fully compatible to cxf.
Instead, org.apache.cxf.common.util.ProxyHelper must be used, with an own Invocation Handler:

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

Also the route needs one processor (without returning of simple types won't work):

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM