Friday, March 12, 2010

The OSGi Bundle Buddy

The Problem

As probably every developer, I want to keep my compile-test-deploy cycle as short and fast as possible. It shouldn't take longer than a few seconds to test a new feature or a bug fix. As long as I have unit tests I can directly run the tests in my IDE or use a tool like sbt (when writing Scala code) to have continuous test execution. However, often I want to evaluate the final product as well, and not only an isolated piece covered by a unit test. Do all my bundles work together? How does the UI look like? How responsive is the AJAX search box?

In order to do this, I need to combine my build artifacts and my target runtime. Since I am a full time OSGi developer, 99% of my build artifacts are bundles. The target runtime varies and depends on the project and task. Sometimes I deploy on Eclipse Equinox, sometimes (and more often recently) I deploy on Apache Felix Karaf and every now and then I embed a OSGi runtime within a WAR and deploy everything in Apache Tomcat. Obviously, executing this cycle takes much longer than running a few unit tests:

  1. Compile the source code
  2. Package all bundles
  3. Assemble/collect all bundles
  4. Copy bundles to target runtime
  5. Make sure the target runtime applies the changes
Especially 3 and 4 are most annoying. What if I only updated one bundle? I can easily do a "mvn package" in the corresponding module directory but I am still forced to execute the top level assembly process. This will collect all bundles, regardless of which I changed, and put them in a common location. From there, I have to manually copy the bundles to the target runtime. Given the presence of a tool like "mvn jetty:run", which automatically watches my class files and restart the web application on changes, I always thought that this process is way to complex. One of OSGi strengths is the dynamic, fine-grained update mechanism so with a bit of plumbing we should be able to improve our build process with it. Tools like fileinstall only help with step 5, since they rely on someone who collects all bundles and copies them to the watched directory.

The Solution

What I wanted is a tool that takes a running target runtime and my build environment and synchronizes them whenever the build changes. This tool should be independent from a specific target runtime (Equinox, Felix, ...) and independent from the build tool (Maven, Ant, Bnd in Eclipse, ...) and it only took approx. 150 lines of code (probably my best LOC/usefulness ratio so far) to implement this: The Bundle Buddy (TBB)

TBB is one simple bundle that you add to your target runtime during the development. Before you start the target runtime, you have set the environment variable "tbb" to the top-level directory of the project you are working on, e.g.

export tbb=/home/roman/workspace/myfancyproject

When you now start the runtime, TBB will recursively collect all jar files in that directory and align the result with the bundles already installed. Whenever a jar file in the build directory gets changed, TBB will update the corresponding bundle in the running OSGi framework automatically. It doesn't matter that the bundle location is different from the build directory path. TBB will synchronize by using the Bundle-SymbolicName and Bundle-Version.

Download

You can get the TBB bundle file at github: http://github.com/downloads/romanroe/tbb/tbb-1.0.1.jar

Roadmap

Currently I have these additional features in mind. Feel free to contact me if you have a fancy idea!
  • Specify more than one directory
  • Watch the class files as well and re-create the bundle jar file on the fly (only works if the manifest does not need to change)