Tuesday, September 09, 2008

Bundling third party dependencies in ServiceMix 4

I'd like to talk a bit about third party dependencies in ServiceMix 4.

ServiceMix 4 is based on OSGi and when deploying applications to it, you have two choices:
  • use the JBI packaging
  • use the OSGi packaging

The first solution is a good thing if you want to abide by the JBI specification completely or if you want to use non OSGi aware components. However, you'll come into the known limitations of the JBI packaging with respect to classloaders: the JBI specification is quite limited in this area.
The second solution is more powerful as, in addition to being able to leverage the OSGi classloader mechanisms, you'll also be able to access the OSGi registry, thus registering or retrieving services in the registry.

However, when using the OSGi packaging, you need to deal with third party dependencies. OSGi is becoming more and more popular, so more and more projects are now distributing the jars as native OSGi bundles. But not all have converted to OSGi yet. And of course, you also need to transform your own jars into OSGi bundles.

Costin Leau has covered most of the aspects of creating OSGi bundles in his blog post. But there are a few tweaks that I want to talk about.

When repackaging a library as an OSGi bundle, one as to take care about package imports. I had a question recently about a problem when repackaging c3p0. This library is a JDBC connection pool, so it needs the class for the JDBC driver to be available in its classpath. A simplistic way to handle the problem is to force an optional import of the package containing the driver you use. This would be something like:
   Import-Package: *, com.mysql.jdbc;resolution:=optional

The drawback is that you tie your mysql bundle to the driver you use, which will break things if you want to switch to another database.
OSGi has a solution for such things which is called Dynamic Imports. This is needed when a library uses the Class.forName() method to load classes dynamically, such as the c3p0 library. The big difference between static imports and dynamic imports is that dynamic imports are resolved ... dynamically. This means that when the Class.forName() method is called, the OSGi framework will look for the requested package and wire it to your bundle at runtime. The OSGi headers would look like:
   Import-Package: *
DynamicImport-Package: *

But this feature must be use with caution, and only when possible to avoid having unwanted wiring between bundles.

A second use case if the legal problems encountered while dealing with proprietary software of non liberal open source licenses. IANAL, so take the following with the appropriate caution and check with your legal department before doing anything. Back to the point: proprietary software often forbid any modification to the jars. In such a case, the only alternative is to embed the jar in an OSGi bundle. Basically, when you repackage a library into an OSGi bundle, you extract the content of the jar, add some OSGi headers to the manifest, and rebuild the jar. There is an alternative though, which is to create a bundle and include the full jar inside it. The bnd tool and felix bundle plugin for maven (which uses bnd) both support that and you end up with using the Bundle-Classpath header:
   Import-Package: *
Bundle-Classpath: .,bin/mysql-connector-java-5.1.6.jar

The only drawback is that the bundle jar can not be used anymore in a non-OSGi environment.

Before bundling a jar, check the existing bundle repositories (the ServiceMix one or the SpringSource one) for an existing bundle. And if you write one for an open source library, feel free to submit a patch if you want ServiceMix to host it...

Happy bundling!


matteoredaelli said...

It would be very useful having in SMX the bundles for the most common databases (mysql, postgres,oracle?,freedts,..)...


Thomas Gill said...

How do you install Oracle jdbc driver jar in ServiceMix/Fuse. Do you have to modify the manifest. I tried putting in lib and also osgi install. Did not work

Guillaume Nodet said...

I think the easiest way to install such a non OSGi driver is to use the wrap pax url handler which is already available in ServiceMix.