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

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

Related

Infinispan file-store creates binary folders with multiple .dat files

I am using the below infinispan configuration file and running into a weird issue. On startup the application creates multiple folders (one for each digit of the byte array 0 to 255) and each folder contains .dat file (in my case idempotent.dat). Also the content of the file are FCS1 for all the files created.
What I was expecting is that it will create a single file (/ap/efts/data/stage/cache/idempotent.dat) which has all the cached objects.
Can someone please identify what is causing this issue? I am using a camel-infinispan component to create Infinispan based idempotent repository.
NOTE: This was working correctly in infinispan version 7.0. For reference, I am also providing infinispan 7.0 configuration file as well as the spring bean configuration for idempotent repository.
infinispan.xml (10.0)
<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:10.0 http://www.infinispan.org/schemas/infinispan-config-10.0.xsd"
xmlns="urn:infinispan:config:10.0">
<cache-container default-cache="idempotent">
<local-cache name="idempotent">
<expiration lifespan="1800000" max-idle="1800000" interval="60000" />
<persistence passivation="true">
<file-store path="/ap/efts/data/stage/cache" purge="true">
<write-behind thread-pool-size="5" />
</file-store>
</persistence>
<memory>
<binary eviction="MEMORY" size="1000000" />
</memory>
</local-cache>
</cache-container>
</infinispan>
infinispan.xml (7.0)
<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:7.0 http://www.infinispan.org/schemas/infinispan-config-7.0.xsd"
xmlns="urn:infinispan:config:7.0">
<cache-container default-cache="default">
<local-cache name="idempotent">
<eviction max-entries="10000" strategy="LIRS"/>
<persistence passivation="false">
<file-store path="/ap/efts/data/stage/cache" max-entries="10000" purge="false">
</file-store>
</persistence>
</local-cache>
</cache-container>
</infinispan>
spring bean configuration
<bean id="cacheManager" class="org.infinispan.manager.DefaultCacheManager" init-method="start" destroy-method="stop">
<constructor-arg type="java.lang.String" value="infinispan.xml"/>
</bean>
<bean id="infinispanRepo" class="org.apache.camel.component.infinispan.processor.idempotent.InfinispanIdempotentRepository"
factory-method="infinispanIdempotentRepository">
<constructor-arg ref="cacheManager" />
<constructor-arg value="idempotent" />
</bean>
It is creating a file for each segment (caches internally are organized in segments). The default is 256. This makes operations such as iteration much faster.
You can disable segmentation at the store level by adding a segmented="false" attribute to the file-store element

Camel exposing CXF Web service

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.

cxf-rs-ws authorization under karaf

I'm trying to configure a cxf soap webservice with authorization and authentication to be deployed on Servicemix.
I configured the LDAP authentication module as follows:
<!-- Bean to allow the $[karaf.base] property to be correctly resolved -->
<ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]"/>
<jaas:config name="myRealm">
<jaas:module className="org.apache.karaf.jaas.modules.ldap.LDAPLoginModule" flags="required">
connection.url = ldap://srv-ldap:389
user.base.dn = ou=people,dc=intranet,dc=company,dc=com
user.filter = (uid=%u)
user.search.subtree = false
role.base.dn = ou=groups,dc=intranet,dc=company,dc=com
role.filter = (member:=uid=%u,ou=people,dc=intranet,dc=company,dc=com)
role.name.attribute = cn
role.search.subtree = true
authentication = simple
</jaas:module>
</jaas:config>
<service interface="org.apache.karaf.jaas.modules.BackingEngineFactory">
<bean class="org.apache.karaf.jaas.modules.properties.PropertiesBackingEngineFactory"/>
</service>
And here is the beans.xml file
<jaxws:endpoint id="myService"
implementor="com.myorg.services.impl.MyServiceWSImpl"
address="/myService">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
</map>
</constructor-arg>
</bean>
<ref bean="authenticationInterceptor" />
<ref bean="authorizationInterceptor" />
</jaxws:inInterceptors>
<jaxws:properties>
<entry key="ws-security.validate.token" value="false" />
</jaxws:properties>
</jaxws:endpoint>
<bean id="authenticationInterceptor"
class="org.apache.cxf.interceptor.security.JAASLoginInterceptor">
<property name="contextName" value="myRealm" />
</bean>
<bean id="authorizationInterceptor"
class="org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor">
<property name="securedObject" ref="securedBean"/>
</bean>
Finally, in my WebService implementation I annotated a method with #RolesAllowed.
#RolesAllowed("Role1")
public Department get(String name) throws IdMException {
return service.get(name);
}
The authentication interceptor is retrieving the user, authenticating it and retrieving the groups as RolePrincipal instances.
Then, in the authorization interceptor (SecureAnnotationsInterceptor), the method configuration is read, the expectedRoles are "Role1", but the SimpleAuthorizingInterceptor.isUserInRole method returns false.
I haven't found any example trying to do more or less the same and the few information I found was from the CXF documentation page http://cxf.apache.org/docs/security.html#Security-Authorization
I have to be missing something important, hope somebody could help me.
Thanks in advance and kind regards.
Your problem is because of Karaf's RolePricipal do not implements Group as CXF expected. Instead of it, it implements Pricipal so CXF thinks that 1st role name is a username. That is why "SimpleAuthorizingInterceptor.isUserInRole method returns false".
A solution is to wait for fixed versions of CXF (2.7.11 and 3.0.0).
If not possible to update to newer version, then an odd and temporary solution (simply workaround) is to add more than one role to a user in LDAP and to method.
You can find more about that bug here: CXF-5603

Is it possible to use both routeContextRef and dataFormats elements in a camelContext?

When I attempt to do so, I get:
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 35 in XML document from class path resource [META-INF/spring/camel-context.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 35; columnNumber: 55; cvc-complex-type.2.4.a: Invalid content was found starting with element 'routeContextRef'. One of '{"http://camel.apache.org/schema/spring":redeliveryPolicyProfile, "http://camel.apache.org/schema/spring":onException, "http://camel.apache.org/schema/spring":onCompletion, "http://camel.apache.org/schema/spring":intercept, "http://camel.apache.org/schema/spring":interceptFrom, "http://camel.apache.org/schema/spring":interceptSendToEndpoint, "http://camel.apache.org/schema/spring":route}' is expected.
Checking the schema, it is apparently true that it's not possible to define routes within a routeContext that can use dataFormat since dataFormats elements must follow routeContextRef elements.
Do I need to abandon the routeContext organizational approach and put all my routes in a single file?
Here is a slightly bowdlerized version of the camel context. I need to be able to use json in cContext.
<?xml version="1.0" ?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
</property>
</bean>
<import resource="classBeanDefs.xml"/>
<import resource="a.xml"/>
<import resource="b.xml"/>
<import resource="c.xml"/>
<camelContext id="camel" trace="false" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties"
location="classpath:route.properties"
xmlns="http://camel.apache.org/schema/spring"/>
<dataFormats>
<json id="json" library="Jackson"/>
</dataFormats>
<routeContextRef ref="aContext"/>
<routeContextRef ref="bContext"/>
<routeContextRef ref="cContext"/>
</camelContext>
</beans>
I get a similar error when I try to add the dataFormats element to the routeContext, which the schema clearly doesn't approve of.
You should have your routeContextRef before the dataFormats.

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