Home Articles OSGi Matters Incompatibility Problems
Tackling Incompatibility Problems of using 3rd Party Components with OSGi Print E-mail
Written by Valery Abu-Eid   

When we say “a component is incompatible with OSGi” we generally mean that the component is unusable from the OSGi Environment – Yes, it's the equivalent of saying: “I can't get the component working inside my OSGi-based application!”. No matter how often you face this problem with different components and how complex or time consuming it can be you will always end up finding that the cause of the incompatibility was one or both of the following problems:

  • Component's class libraries are OSGi incompliant (the JAR files don't contain OSGi headers in their manifest files).
  • The component doesn't behave as expected when running in an OSGi Environment - It will mostly throw some exception that it didn't throw for a similar application running in a non-osgi environment.

Next two sections discuss these problems and different solutions for them.

OSGi Incompliant Class Libraries

As was said before: An OSGi incompliant class library is a JAR file that doesn't contain OSGi headers in it's manifest – I will call these libraries: Non-bundled libraries. A bundle inside an OSGi Environment need to identify itself, expose the classes and other resources it contains and consume classes and other resources exposed by other bundles, for that the bundle describes this information using OSGi Headers in its manifest file. The problem with non-bundled libraries is clear: They don't describe this information. Since OSGi has no support for non-bundled libraries, all the approaches that aim to solve this problem don't differentiate in whether they generate the needed OSGi headers for the non-bundled library or not, but rather when these headers are generated and how.

As you know, you can't add headers to JAR file's manifest but you can create a new JAR file which has the same contents of the original one except for the manifest which would have the additional headers. I will call the process of generating an OSGi compatible JAR file from an OSGi-incompatible one Bundling.

Bundling approaches/solutions can be broke into 3 categories based on their automation level:

  • Manual: You will add OSGi headers manually to class library's manifest file.
  • Semi-Automatic: With such an approach you will usually use a tool to bundle the class libraries. On one hand the utility might generate packages metadata automatically, on the other you will always be the one invoking it each time you decide to include a new library into your project.
  • Automatic: Non-bundled libraries will be identified by a Bundle Generator and bundled automatically without developer's interference. The Bundle Generator can be developed by you (not recommended for the causes I will discuss below), used as 3rd party component or integrated within a framework that will manage your OSGi-based application. With a Bundle Generator, libraries will be bundled either at load time or runtime – It depends on the implementation.

I will discuss the three kind of solutions for bundling class libraries and a different approach in which developers rely on 3rd party vendors for providing them with bundled versions of the class libraries they use.

Manual Bundling

If you have a very small library (a JAR file that contains very few packages) and you don't want to spend time on learning a new tool or approach to bundle it, then it might be suitable for you to add the OSGi headers manually in the manifest file, though in real world applications you will find many libraries that need to be bundled, so it's not practical to bundle them yourself. If you decide to do so, then you need to take into account the following:

  • The mandatory headers that you have to add are: BundleManifest-Version, Bundle-SymbolicName, Export-Package and the needed import headers (i.e., Import-Package, Dynamic-ImportPackage and Require-Bundle).
  • If you can't determine all the packages that the library imports then consider using the header Dynamic-ImportPackage with value '*' so it would import all the packages in the OSGi Environment, though this approach is not recommended in general.
  • Consider adding the version attribute to your imports and exports, especially if there are already other packages in the OSGi Environment that have the same name.

As said before, this can only be suitable when you have one or two bundles with very few packages, but when you use 3rd party libraries, the libraries themselves will mostly have dependencies on other non-bundled libraries.

Semi-Automatic Bundling

Best known tool that falls in this category is Bnd. Although Bnd usually used for creating bundles, it can also be used for bundling non-bundled libraries (wrapping – as it's called in Bnd). The wrap command accepts a JAR file or a directory that contains JAR files as an input and returns a bundled JAR file which contains generated OSGi headers that ideally should work. Following are the good and the bad things about the wrapping functionality of Bnd.

The Good:
  • With any bundling tool like Bnd, you will be able to have bundles of your libraries without launching the application. That's especially useful when testing the application, on the contrary, the Automatic approach might require changes to the design of your unit tests or writing additional code.
  • They have no impact on the performance of the application since libraries are bundled in advance, before the application even is run.
The Bad:
  • Leave the innocent “Hello World!” class library aside, I couldn't get real world components work using Bnd's wrapper command (e.g., JAX-WS, JAXB) – From my experience with bundling different libraries, I think that the problem should be related to package versioning since JAX-WS is already included in JDK 6. Of course that doesn't mean that none of the real world components would work, but if each failure library should consume from a half an hour to few hours of my time to get fixed then the problem is critical.
  • I don't consider bundling libraries with Bnd to be very simple. From my own experience, it took me 30 minutes to get familiar with Bnd enough to try to bundle a real world component like JAXB. Also, I spent a good amount of time trying to figure the meaning of error messages like “Superfluous export-package instructions: [com, com.sun, com.sun.activation, javax]”, “Can not calculate name of output bundle, rename jar or use -properties” and “Did not find matching referal for *”.

Automatic Bundling

Bundle Generators generate bundles from non-bundled libraries at load time or runtime. They need nothing from the developer other than specifying the libraries that need to be bundled. Bundle Generators can be divided into two categories: Simple and Smart Bundle Generators. Simple Bundle Generators are usually implemented with the intention of bundling components that consist of a single class library (a single JAR file), for that case they work fine, but there are no guarantee that they would work with more complex components (components that consist of more than one class library). They almost contain no logic at all and usually fit within a single Java class. This post contains an example of developing a simple Bundle Generator. Smart Bundles Generators on the other hand are intended for complex non-osgi components. They take into account that a component can consist of multiple libraries, package versioning, the existence of library's packages in application's classpath and other problems related to running a non-osgi component in an OSGi Environment.

I never noticed the difference between Bundle Generators unless I developed a bundle generator for DA-Launcher. I started with a simple Bundle Generator like the ones I've seen in other projects and every thing seemed fine during tests – Every thing seemed fine during tests due the fact that I was testing a “Hello World!” bundle all the way. But when I wanted real world non-osgi components get to work on DA-Launcher using the Bundle Generator I fell into countless problems. At the end I realized that I've spent time on the development of the working version of the Bundle Generator 15-20 times more than I've spent on the simple one and that there is a clear difference between an implementation of a Bundle Generator that works for components which consist of a single library and an implementation that works for components which consist of several libraries. It's worth mentioning that during the course, I was able to run in DA-Launcher more than 9 major non-osgi components that contained more than 130 class libraries – Actually, after running a little more than 100 libraries I reached to the point where I couldn't face new problems with bundles wiring, at that moment I was sure that the Bundle Generator works fine enough to be released. Below are the good and the bad things about Bundle Generators in general and the Bundle Generator of DA-Launcher specifically.

The Good:
  • Very simple to use and development efficient. For instance, if you're using DA-Launcher then all you need to have a component like JAX-WS (which consists of about 20 class libraries) bundled and installed is copying it's JAR files to a specific directory. Such an approach requires only few seconds of your time and doesn't require reading documentation. If you use some other Bundle Generator then all you will need mostly is specifying library's file name.
  • Talking specifically about the Bundle Generator of DA-Launcher, it's well tested. At the time of writing this article more than 200 class libraries were bundled using it, these libraries were for components like JAX-WS RI, JAXB RI, Apache CXF, Apache Axis2, JPA, Toplink, H2 database, Apache Derby, HSQLDB, MySQL JDBC Connector, Jetty, etc. and so far, I wasn't able to encounter any problems with bundling OSGi incompliant class libraries.
The Bad:
  • Installing non-bundled libraries with a Bundle Generator at runtime consumes more time than installing a typical bundle, so if you have a lot of libraries (more than 30) that need to be bundled and installed when the application is started the load time will increase notably. As for DA-Launcher's Bundle Generator, future versions will be using caching techniques to overcome this problem.
  • Since bundles are generated at runtime you don't have them outside the scope of the application, as such, if you want to perform unit tests that use these bundles you will need to use the Bundle Generator inside your unit tests.

Relying on 3rd party vendors for Bundling Class Libraries

The only company that provides such a service is SpringSource with it's SpringSource Bundle Repository. The Spring Source Repository contains bundled versions of many components. Below are the Good and the Bad things about SpringSource Repository.

The Good:
  • The Spring Source Repository saves you the effort of bundling 3rd party components libraries in advance with a bundling tool.
The Bad:
  • You will never be able to have the latest version of the component (at the time of writing this article the latest version of H2 database was 1.0.74 and the latest version of the same component in SpringSource Repository was 1.0.64, it's worth mentioning that the difference between the release dates of these versions is more than 6 months, so in the case of H2 the repository is behind in more than 6 months and it's not clear how far behind it will stay).
  • SpringSource Repository doesn't contain all of the popular components yet, for instance, JAX-WS RI (The Reference Implementation of Java XML API for Web Services) is not there.
  • Some components will never find a place in the SpringSource Repository because of licensing issues (e.g., licenses that restrict the redistribution and modification of component's files).
  • Generated bundles have different file names and maven artifact names than the original ones (they are usually prefixed). Following post is an example of this problem.

Unexpected Behavior of non-osgi Components

Unfortunately, even if you managed to have non-osgi component's libraries bundled and wired correctly you might still have some problems concerning the behavior of the component. Theoretically it can be any problem caused by the differences between OSGi and non-osgi environments, but on practice you will find that most of the problems, if not all of them, are class or resource loading exceptions. The cause of the problem is that many components like JAX-WS, JAXB, Toplink, etc. load some of the classes they need and classes provided by the user from the context class loader of the thread they run in. For instance, when Toplink tries to load an entity class described in the persistence.xml file it will look for it in the context class loader of the thread where it runs.

Below are the most common indicators of a class/resource loading problem:

  • Having ClassNotFoundException in the exception chain of a thrown exception.
  • Having an exception that describes a failure of locating a service provider. This is a typical problem when using an OSGi incompliant JSR class library (e.g., JPA, JAXB, STAX, etc.). For instance, if you use JPA and try to create an entity manager factory without setting thread's context class loader to a one that will load resources from the OSGi environment then the JPA API will fail to create the entity factory due the lack of an entity provider even if you have Toplink installed as a bundle.
  • Any problem resulted by the inability of locating a file, e.g., Toplink complaining about the inability of locating the persistence.xml file.

The most suitable solution for this problem would be to set thread's context class loader to a class loader that loads classes and resources from the OSGi Environment before invoking classes provided by an OSGi-incompliant component that will probably load some resources – For the sake of simplicity I usually set the class loader in the start method of my Bundle Activator. While you can always develop your own class loader, generally it's not recommended since you will mostly fall into many unexpected problems along the way – If you find yourself in a position where you need to develop your own, it's better to extend an existing one rather than beginning from scratch. The The ClassLoading-Utils project provides a bundle that defines a class loader which loads classes and resources from the bundles of the OSGi environment. This class loader is optimized and is used by all of the projects and examples at DynamicJava.org that run OSGi-incompatible components – In other words it proved to work with more than 200 non-bundled class libraries. The ClassLoading-Utils bundle is very lightweight (by having the minimal amount of classes and no dependencies on other bundles), it weighs less than 20 kilobytes.

This example contains an application that uses JAXB, if you look at the source code you will find how the class loader was set to tackle this problem. A similar example for JPA can be downloaded from here.

Summary

The process of tackling incompatibility problems when using an OSGi-incompatible component in an OSGi Environment involves two steps:

  • Bundling component's class libraries if they are OSGi incompliant (have no OSGi headers).
  • Fixing component's runtime problems which mostly will be class/resource loading issues.

Bundling component's class libraries mostly will be done either by a Bundle Generator that will bundle the libraries at runtime or a tool that the developer will use to bundle the libraries before running the application. Fixing class/resource loading issues for OSGi-incompatible components can be solved by making a class loader which loads classes and resources from the OSGi environment as the context class loader of the thread that invokes the component.