Continuing from yesterday’s post about Maven, I’m going to briefly discuss our approach to handling OSGi.
Every module that we build, whether it’s a JAR or a WAR, is packaged as an OSGi bundle. Originally we relied on the maven-bundle-plugin from Apache Felix to generate the MANIFEST.MF files every time the package phase was executed. As the manifest was dynamically generated, we were careful to never check in the generated manifest, as it would result in errors when a developer or our continuous integration system attempted to build the product and would inadvertently clobber the read-only manifest file.
As part of the plug-in configuration we originally hand-coded each and every package import, although recently we’ve been relying more on bnd’s ability to scan bytecode and only adding imports for packages referenced from Spring application contexts or other non-bytecode sources. This has made the process much easier, but there is still a chance that you could end up with a ClassNotFoundException or NoClassDefFoundError if you’re not careful (more on that in a moment).
We have also recently switched to generating the manifest file once and then checking it into Perforce. The static manifest means that when we import the modules into Eclipse we can take advantage of the PDE tooling to edit and maintain the manifest. It also allows us to have the modules (bundles) automatically added to the Eclipse target platform, making it easy to run the product and debug it from within the Eclipse IDE.
One downside to these approaches is that there is still duplicated metadata between Maven and the maven-bundle-plugin/OSGi because they do not share a common metadata source. Our practice of relying on bnd to pick up most imports has minimized this to some degree. We expect that future improvements to Maven, PDE, and other OSGi tooling will eliminate the problem entirely.
OSGi metadata is challenging to get right the first time, so early on we established the practice of creating OSGi integration tests for each bundle that was produced. The purpose of the tests is to verify that the correct packages are being imported/exported from a bundle, that no implementation classes slip into the export list, and that any services or service references are correctly registered/resolved. One of my colleagues wrote an abstract class that takes away much of the pain of programmaticaly starting Eclipse Equinox and automatically loading in our third-party dependencies, so all the individual test authors needs to do is essentially reproduce the OSGi metadata/contract in JUnit form (we’re currently using Spring DM’s OSGi tests support).
So far the tests have been rather successful at identifying missing dependencies prior to deployment. The only real downside to this approach is the duplication of metadata in the test cases.
- Rely on bnd’s bytecode scanning technique to pick up most package imports instead of explicitly adding them to the maven-bundle-plugin’s configuration.
- Explicitly add packages referenced from XML files (Spring, Hibernate, etc.) and for classes that are dynamically loaded. Hibernate or other frameworks that use cglib/javassist can be particularly difficult to get right if you’re not extremely careful.
- Make sure your modules always have a manifest file with OSGi metadata so you can take advantage of the Eclipse PDE tooling.
- Run your bundles in an environment as similar as possible to your target platform prior to deploying in production so you can ensure that all the necessary dependencies have been specified and are present in the environment.