Tech stack:
Fuse 6.x, OSGi Services, Spring 3.x – 4.x, Java 7/8
Links:
Spring-DM pom
spring-osgi-service-adapter
example code
download Fuse
Spring-DM is the common name for the Spring Framework jars responsible for Spring/OSGi integration. Their most recent update according to Maven Central was in December 2009 with update version 1.2.1. Spring-DM is implemented with Spring version 2.5.6.SEC01 and is supposed to be compliant with Spring versions up to 3.x.
As of August 2016 Red Hat Fuse on Karaf/Felix (OSGi container) is still being packaged with the full compliment of Spring jars and the most recent version of Spring-DM. What’s going on? Why is a modern product still relying on software that was last updated more than 6 years ago? To answer this question, 1st a brief history of the products is in order.
Spring offloaded Spring-DM to Eclipse around 2010 due to disagreements between the OSGi and Spring Source developers. When this happened, the project was officially renamed the Eclipse Gemini Blueprint project. In effect, Spring-DM support was killed and the OSGi developers created a competing dependency injection framework similar to Spring. These two frameworks (Spring and Blueprint) are incompatible. The primary drawback of this is that beans managed by Spring cannot be referenced from a Blueprint context and vice versa.
One of the main uses of Spring-DM was that it made exposing and using OSGi services very easy. Blueprint enables this functionality with the same ease of use as Spring-DM. If at all possible, it is recommended that one uses Blueprint to expose and consume OSGi services. However, this is not always possible. Often an organization will be locked into Spring because all their applications use that framework. Also, Spring provides a lot of functionality that Blueprint does not, such as prototypes and managed bean inheritance.
Fuse supports both Spring and Blueprint as dependency injection frameworks, however developers will find trying to use OSGi services with the Spring framework difficult if not impossible without Spring-DM, or writing a lot of OSGi code in their application classes (not recommended). What is one to do?
My personal experience with this dilemma was that it was unavoidable to end up writing some OSGi specific code. I couldn’t expose an OSGi service just from the context, and I definitely couldn’t consume an OSGi service without writing some sort of OSGi code in the Spring bean that needed the OSGi service instance. Often, a BundleActivator class needed to be used. This was unacceptable to me. OSGi is a platform specific framework and should not require writing special code in the application classes.
I set out to solve this problem under the following criteria:
- The new Spring/OSGi service integration pattern had to be simplistic and simple.
- The new method had to expose and consume services solely from the Spring applicationContext configuration.
- No java code should be written to expose or consume services.
- The new method had to be independent of any Spring libraries so it could be used with any newer version of Spring.
- The configuration in the applicationContext had to be simple and intuitive.
What I came up with was a new library I titled spring-osgi-service-adapter. The library meets all the criteria I set out for it. Now, this library isn’t perfect and doesn’t meet all situations, but I think it will satisfy most use cases and the pattern used can be customized to fit one’s particular application needs. There are links above for example code and the library itself. There are verbose Javadocs in the source files as well which should explain in greater detail what’s going on and how to use the library.
To expose a service:
<bean id="osgiServiceRegistrator" class="com.github.mshin.sosa.OsgiServiceRegistrator"
init-method="init" destroy-method="destroy">
<property name="serviceInstance">
<bean class="com.example.osgi.service.spring.MyNoInterfaceSpringOsgiService" />
</property>
</bean>
(If no properties, and serviceInstance will be retrieved using its same class).
or:
<bean id="osgiServiceRegistrator" class="com.github.mshin.sosa.OsgiServiceRegistrator"
init-method="init" destroy-method="destroy">
<property name="bundleClass">
<value type="java.lang.Class">com.example.osgi.service.spring.impl.ClassInOsgiServiceSpringBundle
</value>
</property>
<property name="osgiServiceDefinitionList">
<list>
<bean class="com.github.mshin.sosa.OsgiServiceDefinition">
<property name="classList">
<list>
<value>com.example.osgi.service.spring.MySpringOsgiService</value>
</list>
</property>
<property name="serviceInstance">
<bean class="com.example.osgi.service.spring.impl.MySpringOsgiServiceImpl" />
</property>
<property name="properties">
<map>
<entry key="property1" value="value1" />
<entry key="property2" value="value2" />
</map>
</property>
</bean>
</list>
</property>
</bean>
Where bundleClass is a class in the bundle, the OSGi service is registered under classList index 0, serviceInstance is the exposed object, and properties are properties associated with the service.
To consume the two services exposed above:
<bean id="cachingOsgiServiceGetter" class="com.github.mshin.sosa.CachingOsgiServiceGetter"
init-method="init" destroy-method="destroy">
<property name="bundleClass">
<value type="java.lang.Class">com.example.osgi.service.use.spring.UseServices</value>
</property>
<property name="findOsgiServiceTimeout" value="10000" />
</bean>
<bean id="mySpringOsgiService" class="com.github.mshin.sosa.OsgiService" factory-method="getService">
<constructor-arg index="0" value="com.example.osgi.service.spring.MySpringOsgiService" />
<constructor-arg index="1" value="(&(objectclass=com.example.osgi.service.spring.MySpringOsgiService)(property1=value1))" />
<constructor-arg index="2" ref="cachingOsgiServiceGetter" />
</bean>
<bean id="myNoInterfaceSpringOsgiService" class="com.github.mshin.sosa.OsgiService" factory-method="getService">
<constructor-arg value="com.example.osgi.service.spring.MyNoInterfaceSpringOsgiService" />
<constructor-arg ref="cachingOsgiServiceGetter" />
</bean>
<bean id="useServices" class="com.example.osgi.service.use.spring.UseServices">
<property name="mySpringOsgiService" ref="mySpringOsgiService" />
<property name="myNoInterfaceSpringOsgiService" ref="myNoInterfaceSpringOsgiService" />
</bean>
Where bundleClass is a class in the bundle, userServices is a class in the bundle that needs the OSGi services injected, myNoInterfaceSpringOsgiService is an OSGi service that is found using it’s class name, and mySpringOsgiService is a bundle found using an LDAP style filter. If the filter was not present, it would be found using its interface, MySpringOsgiService.
The hardest part of coming up with this strategy was coming up with an intuitive way to extract the OSGi service from the CachingOsgiServiceGetter. What I needed was a Spring parameterized instance factory-method. the getService method cannot be static and requires parameters to return different types of OSGi services. (factory-method can only be non-static if a factory-bean is used). These services must be injectable into Spring managed application beans. The method I came up with is a bit tricky. I will go into it in more detail in a later blog post, but I took advantage of the fact that factory-method doesn’t necessarily return the type in which the method is declared.
I was always annoyed that Spring was supported on Fuse except for OSGi services. Thankfully, there is now a solution (if imperfect) that doesn’t require rewriting Spring-DM. One should be able to save a lot of time and frustration by using the spring-osgi-service-adapter library, or at least be able to use some of the ideas in it for one’s own Spring/OSGi services solution.
Referenced Jars:
com.github.mshin/spring-osgi-service-adapter/1.0
org.springframework.osgi/spring-osgi-io/1.2.1
org.springframework.osgi/spring-osgi-core/1.2.1
org.springframework.osgi/spring-osgi-extender/1.2.1
org.springframework.osgi/spring-osgi-annotation/1.2.1