Camel exposing CXF Web service - apache-camel

I'm trying to expose a JAX-WS Webservice (an annotated Java class) using Camel. When using a single parameter the Web service replies correctly. On the other hand when using as parameter an Object or multiple parameter it does not work.
Here is the blueprint I have deployed on JBoss Fuse:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:camelcxf="http://camel.apache.org/schema/blueprint/cxf" xmlns:cxf="http://cxf.apache.org/blueprint/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://camel.apache.org/schema/blueprint/cxf http://camel.apache.org/schema/blueprint/cxf/camel-cxf.xsd http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<camelcxf:cxfEndpoint id="demo-target-cxf" address="http://localhost:9000/SourceExample/hello" serviceClass="com.sample.SourceExampleWSImpl" endpointName="SourceExamplePort" serviceName="SourceExampleService" />
<camelContext xmlns="http://camel.apache.org/schema/blueprint" id="fuse-demo-route-exmple-cxf">
<route id="demo-target-cxf">
<from uri="cxf:bean:demo-target-cxf" />
<transform>
<simple>${in.body}</simple>
</transform>
<log message="Message input: ${in.body}" />
<removeHeaders pattern="CamelHttp*" />
</route>
</camelContext>
</blueprint>
Here is the Web Service implementation class:
#WebService
public class SourceExampleWSImpl {
#WebMethod
public int getTotal(int x, int y) {
return x+y;
}
}
The bundle ic correctly deployed on JBoss Fuse. When invoking the Web service, only the first parameter is evaluated. So for example, invoking with arguments 1 and 4:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://sample.com/">
<soapenv:Header/>
<soapenv:Body>
<sam:getTotal>
<arg0>1</arg0>
<arg1>4</arg1>
</sam:getTotal>
</soapenv:Body>
</soapenv:Envelope>
returns:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getTotalResponse xmlns:ns2="http://sample.com/">
<return>1</return>
</ns2:getTotalResponse>
</soap:Body>
</soap:Envelope>
Any idea how to fix it ?
Thanks

Do you mean the expected output should be
1 + 4 = 5 because the following code should be called?
public int getTotal(int x, int y) {
return x+y;
}
If so that does not happen, when you use camel-cxf as a bean then the bean only defines the contract, the code in the implementation is not in use.
If you just want a standard SOAP-WS and write java code that builds and process the SOAP requests/response then just use plain CXF.

Related

How to share cxf:bus over multiple osgi bundles on karaf?

In my application, I have 2 bundles. Both are using cxf to create Restful server.
In those bundles I load cxf via blueprint. I define cxf:bus on those bundles with the same id to expect that two bundle will share the same bus instance then I can configure a Authentication interceptor on one bundle and It will apply for the other bundle also.
They looks like below.
Bundle 1:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
xmlns:cxf="http://cxf.apache.org/blueprint/core"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd
http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd
">
<bean id="authInterceptor" class="com.dasannetworks.vn.rest.impl.AuthInterceptorImpl"></bean>
<cxf:bus id="my-app-bus" name="tutorial">
<cxf:inInterceptors>
<ref component-id="authInterceptor"/>
</cxf:inInterceptors>
</cxf:bus>
<bean id="Rest1ServiceImpl"class="com.dasannetworks.vn.rest.impl.Rest1ServiceImpl"></bean>
<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
<property name="serializeAsArray" value="true"/>
<property name="dropRootElement" value="true"/>
<property name="supportUnwrapped" value="true"/>
</bean>
<jaxrs:server id="custom1Service" address="/rest1">
<jaxrs:serviceBeans>
<ref component-id="rest1ServiceImpl"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref component-id="jsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
Bundle 2:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
xmlns:cxf="http://cxf.apache.org/blueprint/core"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd
http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd
">
<cxf:bus id="my-app-bus" bus="tutorial"></cxf:bus>
<bean id="rest2Service" class="com.dasannetworks.vn.rest2.impl.Rest2ServiceImpl" />
<jaxrs:server id="custom2Service" address="/rest2">
<jaxrs:serviceBeans>
<ref component-id="rest2Service"/>
</jaxrs:serviceBeans>
After installing and run:
The outcome:
all rests request to "/cxf/rest1/" will run into authInterceptor whereas all rests request to "cxf/rest2" are not.
Could anyone give me some advice about how to share the same cxf:bus on both bundles ?
Thank you in advance.
Since I cannot solve this problem with blueprint, I changed the way to fix it.
Instead of blueprint, I initiated CXF Bus Rest Server with Activator.
Bundle 1 Activator:
In bundle 1, I get the default bus and add authentication interceptor on it then register a Rest Server endpoint on it.
public void start(BundleContext bundleContext) throws Exception {
Bus defaultBus = BusFactory.getDefaultBus();
defaultBus.setId("my-app-bus");
BusFactory.clearDefaultBusForAnyThread(defaultBus);
System.out.println(this.getClass().getName());
System.out.println(defaultBus.getId());
defaultBus.getInInterceptors().clear();
defaultBus.getInInterceptors().add(new AuthInterceptorImpl());
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setBus(defaultBus);
sf.setAddress("/tutorial");
sf.setServiceClass(HelloRestServiceImpl.class);
sf.create();
}
In bundle 2,
Bundle 2 Activator
I get the default bus, register and set that bus for Rest Server.
#Override
public void start(BundleContext bundleContext) throws Exception {
Bus defaultBus = BusFactory.getDefaultBus();
List<Interceptor<? extends Message>> inInterceptors = defaultBus.getInInterceptors();
for(Interceptor interceptor: inInterceptors) {
System.out.println( interceptor.getClass().getName());
}
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setBus(defaultBus);
sf.setAddress("/Rest2");
sf.setServiceClass(Rest2ServiceImpl.class);
sf.create();
}
==> The result : both bundles are now use the same bus, and bundle 2 can run into authentication interceptor which I registered on bundle 1 also. !!!

How to explicitly define Apache Camel CXF Consumer and Producer

I am experiencing an interesting dilemma with Apache Camel and CXF.
I try to build following routes.
from("servlet://proxy?matchOnUriPrefix=true")
.to("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE");
`from("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE").to("file:/META-INF/data/test.xml");`
My wish actually is to receive the request with Servlet Component, do something with (processor, transform, etc but I excluded here for simplicity reasons) redirect this to my WebService implementation and the file component return the content of a file as a result to WebService.
The problem is, if I make from Endpoint 'from("servlet://proxy?matchOnUriPrefix=true")' routing to Endpoint 'to("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE")' Camel thinks this should be CXF Consumer but I want it to be a Producer.
So I actually I like to route from from("servlet://proxy?matchOnUriPrefix=true") to 'from("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE")' but I could not figure out a way to do it.
If I configure as the first configuration, the servlet component receives the request, routes to CXF:consumer then it starts a TCP call to CXF:Producer defined in 'from("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE")' it works but it is weird, I don't want to start a TCP call, I just want that the message goes to 'from("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE")'.
Is there a way to achieve what I want? Can I define a CXF Consumer or Provider other then using 'from', 'to'?
Funnily I did something similar with ServiceMix/Fuse.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:osgiRouter="http://osgi.salgar.org/osgirouter"
xmlns:http="http://servicemix.apache.org/http/1.0"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://servicemix.apache.org/http/1.0 http://servicemix.apache.org/http/1.0/servicemix-http.xsd">
<http:consumer service="http:FindOrders" endpoint="httpEndpoint" locationURI="http://localhost:8088/MockB2BService"
defaultMep="http://www.w3.org/2004/08/wsdl/in-out" targetService="osgiRouter:mediation" />
<bean class="org.apache.servicemix.common.osgi.EndpointExporter" />
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:eip="http://servicemix.apache.org/eip/1.0"
xmlns:osgiRouter="http://osgi.salgar.org/osgirouter"
xmlns:orderProvider_v1="http://v1.salgar.org/B2BService"
xmlns:orderProvider_v2="http://v2.salgar.org/B2BService"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://servicemix.apache.org/eip/1.0 http://servicemix.apache.org/eip/1.0/servicemix-eip.xsd">
<eip:content-based-router endpoint="osgiRouterMediation"
service="osgiRouter:mediation">
<eip:rules>
<eip:routing-rule>
<eip:predicate>
<eip:xpath-predicate id="wsdl_version_predicate_v1"
xpath="(//namespace::*[.='http://v1.salgar.org/B2BService']) = 'http://v1.salgar.org/B2BService'" />
</eip:predicate>
<eip:target>
<eip:exchange-target service="orderProvider_v1:findOrders" />
</eip:target>
</eip:routing-rule>
<eip:routing-rule>
<eip:predicate>
<eip:xpath-predicate id="wsdl_version_predicate_v2"
xpath="(//namespace::*[.='http://v2.salgar.org/B2BService']) = 'http://v2.salgar.org/B2BService'" />
</eip:predicate>
<eip:target>
<eip:exchange-target service="orderProvider_v2:findOrders" />
</eip:target>
</eip:routing-rule>
</eip:rules>
</eip:content-based-router>
<bean class="org.apache.servicemix.common.osgi.EndpointExporter" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxfbc="http://servicemix.apache.org/cxfbc/1.0"
xmlns:orderProvider_v1="http://v1.salgar.org/B2BService"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://servicemix.apache.org/cxfbc/1.0 http://servicemix.apache.org/cxfbc/1.0/servicemix-cxf-bc.xsd">
<cxfbc:provider endpoint="findOrdersProviderEndpoint"
useJBIWrapper="false" wsdl="classpath:v1/B2BService.wsdl" service="orderProvider_v1:findOrders"
locationURI="http://localhost:8989/MockB2BService" />
<bean class="org.apache.servicemix.common.osgi.EndpointExporter" />
</beans>
Which works as expected but there I could define the endpoint explicitly as CXF provider, do I have the same possibility here?
If I understand your problem right, this will work (and ideal solution in camel)
Route A
from("servlet://proxy?matchOnUriPrefix=true")
.to("direct:webservice-business-logic")
.to("direct:process-to-file");
Route B
from("direct:webservice-business-logic")
.to("actual-processors-to-do-webservice-business-logic");
Route C
from("cxf:/incident?wsdlURL=wsdl/OrderInfoService.wsdl&dataFormat=MESSAGE")
.to("direct:webservice-business-logic);
Basically, Route C will be used only by external parties who wants to use the webservice.
Route B will be used only by Route A, So Route B is meant for Internal camel processing. Because there is no point in calling a webservice , where the webservice logic also sits in the camel route.
Hope this helps !

Camel CXF + JAX-WS + SoapHeader

How can I parse SOAP headers within apache camel processor from apache camel cxf endpoint in POJO mode:
<cxf:cxfEndpoint id="MyWS" address="http://localhost:8080/services/MyWS" serviceClass="tmp.MyWS" />
<route>
<from uri="cxf:bean:MyWS" />
<process ref="MyProcessor" />
</route>
On the official apache camel cxf website I found an example how to set headers, but there is no examples about getting them. How can I get messageId value. My SOAP request looks like that:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inv="http://inv.org">
<soapenv:Header>
<inv:messageId>111</inv:messageId>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
In the Camel Route builder we will get the SOAP Header using simple componenent, in the below example I am apply cbr using SOAP operation name i.e.
from("cxf:bean:xyz-ws?dataFormat=POJO").routeId("xyz-ws").log( "This is ${in.header.operationName} operation called...." )
.log( "Entering inside the Choice with operation....${in.header.operationName}")
.when(simple("${in.header.operationName} == 'getDataFromDictionary'"))
Hope this is helpfull.

Want to build a route which will check contents of Soap request and pass the request to appropriate URI

I am Ashish from Mumbai and very new to Apache Camel.
Currently I am building a route in XML which will scan the SOAP request and will redirect the request to appropriate URI.
My Soap request isn as follows:
<service xmlns="http://ws.madcomum.comprova.com">
<request>
<keysValues>
<item>
<bytesValue
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<dateValue
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<doubleValue
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<key>validatesOriginIntegrity</key>
<longValue
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<stringValue>z4x/FOOR+EPQ0vD9+itPSCBeNR4=</stringValue>
</item>
</keysValues>
<actionId>1</actionId>
<clientId>ARGO</clientId>
</request>
</service>
From this SOAP envelope, I want to parse out value of actionId tag using Camel Route.
If actionId has value of 1 then route must be redirected to callService else to another service.
I developed logic of route as folows:
<route>
<from uri="cxf:bean:comprovaWS?dataFormat=MESSAGE" />
<when>
<xpath>//actionId=1</xpath>
<to uri="log:input" />
<to ref="callService" />
<to uri="log:output" />
</when>
<otherwise>
<to uri="log:input" />
<to ref="otherService"/>
<to uri="log:output" />
</otherwise>
</choice>
</route>
But this logic is not working.
Is there any error in my route?
Though I am Java guy, I don't want to use Java here. I want to rely on Camel itself.
Please help me ASAP.
Regards,
Ashish
When you use xpath then 95% of the times when people have trouble its often due to namespaces. Your SOAP message is defined using a namespace - "http://ws.madcomum.comprova.com". The xpath expression must use this namespace to make it work.
See more details at: http://camel.apache.org/xpath, there is an example at the section Using XML configuration
Also as you use CXF in MESSAGE mode, then read about stream caching as the message is stream based: http://camel.apache.org/stream-caching.html

Configuring WSS4J with CXF

I was doing pretty well with setting up a contract first set of web services using CXF until I started adding in the WSS4J piece.
I'm trying to debug sending a password and login in the soap header. I am getting null when I call getPassword() in the WSPasswordCallback class. I can see from the soap envelope that a password was sent.
This post, http://old.nabble.com/PasswordDigest-and-PasswordText-difference-td24475866.html, from 2009, made me wonder if I am missing (need to create) a UsernameTokenHandler.
And if that is true, can someone point me to how I would configure it in the spring/cxf bean xml file?
Any advice or suggestions would be appreciated.
Here's the Java file in question:
package com.netcentric.security.handlers;
import java.io.IOException;
import javax.annotation.Resource;
import javax.com.urity.auth.callback.Callback;
import javax.com.urity.auth.callback.CallbackHandler;
import javax.com.urity.auth.callback.UnsupportedCallbackException;
import org.apache.ws.com.urity.WSPasswordCallback;
public class ServicePWCallback implements CallbackHandler
{
#Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
try {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
sString login = pc.getIdentifier();
String password = pc.getPassword();
// password is null, not the expected myPASSWORD**1234
int n = pc.getUsage();
// this is 2 == WSPasswordCallback.USERNAME_TOKEN
//...
The CXF/Spring configuration file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"
default-dependency-check="none" default-lazy-init="false">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="serverPasswordCallback" class="com.netcentric.security.handlers.ServicePWCallback"/>
<bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="passwordCallbackRef">
<ref bean="serverPasswordCallback"/>
</entry>
</map>
</constructor-arg>
</bean>
<jaxws:endpoint id="FederationImpl"
implementor="com.netcentric.services.federation.FederationImpl"
endpointName="e:federation"
serviceName="e:federation"
address="federation"
xmlns:e="urn:federation.services.netcentric.sec">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>
<ref bean="wss4jInInterceptor"/>
</jaxws:inInterceptors>
</jaxws:endpoint>
</beans
The soap message:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<wsse:comurity xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wscomurity-comext-1.0.xsd" soapenv:mustUnderstand="1">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wscomurity-utility-1.0.xsd" wsu:Id="Timestamp-16757598">
<wsu:Created>2011-09-22T18:21:23.345Z</wsu:Created>
<wsu:Expires>2011-09-22T18:26:23.345Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wscomurity-utility-1.0.xsd" wsu:Id="UsernameToken-16649441">
<wsse:Username>pam</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">myPASSWORD**1234</wsse:Password>
</wsse:UsernameToken>
</wsse:comurity>
</soapenv:Header>
<soapenv:Body>
<getVersion xmlns="urn:federation.services.netcentric.com">
<getVersionRequest/>
</getVersion>
</soapenv:Body>
</soapenv:Envelope>
If using CXF 2.4.x, I would recommend reading:
http://coheigea.blogspot.com/2011/02/usernametoken-processing-changes-in.html
and seeing if that helps provide some extra information. Colm's blog is a treasure trove of useful info about recent WSS4J releases.
Thanks for all your help. I have made progress on this. I am using CXF 2.4.2 and WSS4J 1.6.2. The framework now takes care of checking the password for you. So the correct inside section is
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
sString login = pc.getIdentifier();
String password = getPassword(login);
pc.getPassword(login);
//...
}
Instead of retrieving the password from the soap header to compare against the expected value, you lookup the expected value and pass it to the framework to do the comparison.
I add this :
// set the password on the callback.
This will be compared to the
// password which was sent from the client.
pc.setPassword("password");
==> the password between "" will be compared with password sended by client.
Client side: write login = bob ; Password = bobPassword (will be digested)
Server side: Catch user = bob and the function user.setPassword(bobPassword) verify if the password received is correct or no.

Resources