OSGi at LinkedIn – Bundle repositories
Code Alert! This is a part of our continuing series on Engineering at LinkedIn. If this isn’t your cup of Java, check back tomorrow for regular LinkedIn programming. In the meanwhile, check out some of our recent announcements, tips and tricks, or success stories.
When you start using OSGi, the very first problem you are going to be faced with, is the fact that OSGi requires bundles. A bundle is nothing more than a jar file with extra manifest information. Here is a ‘typical’ example of a manifest for an OSGi bundle (the entries in bold are the OSGi specific headers).
. com.linkedin.colorado.helloworld.client.HelloWorldClientActivator
Import-Package:
. com.linkedin.colorado.helloworld.api;version="[1.0.0,1.0.1)",
. com.linkedin.colorado.helloworld.client;version="[1.0.0,1.0.1)",
. org.osgi.framework;version="[1.4.0,1.4.1)"
Export-Package:
. com.linkedin.colorado.helloworld.client;version="1.0.0"
Bundle-Version: 1.0.0
Bundle-Name: colorado-helloworld-client
Bundle-ManifestVersion: 2
Bundle-SymbolicName: colorado-helloworld-client
Bnd-LastModified: 1224557973403
Generated-On: 20081020
Tool: Bnd-<unknown version>
Implementation-Version: DevBuild
This is how you instruct the OSGi container about your dependencies (Import-Package), what you provide (Export-Package), how you become active (Bundle-Activator), etc…
So why did I start this post by saying it was a problem ? The answer is actually two-fold:
- you need to generate those headers for your own jar files and it can be quite challenging if you want to do it manually (our biggest bundle currently has over 760 import package entries!)
- all external libraries that you require also need to be a bundle (libraries that you do not control like log4j, xerces,…)
In this post I will be concentrating on problem #2 and I will come back to problem #1 in a later post.
Let’s start with some numbers. As of this writing (January 2009), our repository of external libraries contains 200 jar files. Only 8 of them are bundles out of the box (4%). I believe that this small sample reflects the harsh reality out there: over 95% of the available libraries are not bundle.
So what is the solution? The answer is unfortunately not that easy. For starters, you should definitely check the SpringSource bundle repository that they are offering for free. It contains a good list of libraries that have been converted to bundles (they even have a full time employee just for this ongoing task!). One of the big issue is that it is hard to keep up with as new libraries are popping up on a daily basis include snapshots. It’s benefits are debatable but in practice, sometimes you just don’t have a choice! In our case, we just cannot afford to rely solely on the availability of bundles. So here is the approach that we took:

What we are trying to achieve is to convert a repository of libraries (96% jar files (blue)) into a repositories of bundles (100% bundles (red)). For this we use bnd, ivy and some custom code.
Our repository of external libraries is using ivy for dependency management (note that the process would be very similar with maven). Using ivy resolution, it is relatively easy to build the (non cyclic) graph of dependencies between all the libraries. All the leaves represent libraries that do not have dependencies on other libraries (Step1).

BND is a tool that analyzes a jar file and can create OSGi manifest headers. In Step 2, we iterate over each leaf and we feed it to bnd to generate a bundle as a result. We use some custom code (ant tasks) to have more control over what is provided as input to bnd and the errors/warnings that we need. In Step 3, we repeat the same process one level up the dependency graph. This time we know we are dealing with libraries that have dependencies, but we also know that they have properly been converted into bundles, so the classpath (which is one of the input to bnd) will contain only proper bundles. With the proper classpath, bnd will be able to generate the proper manifest entries (the version and resolution attributes of the Import-Package entries will be correct). And recursively we go all the way up the chain of dependencies until we have converted the entire repository.
Overall this process works quite well but there are several issues that I want to point out:
- The result clearly depends on the quality of the original repository in terms of dependencies. If the dependencies are wrong or missing, then the end result will be of lesser quality with the "resolution:=optional" attribute being set which can lead to the dreaded NoClassDefFoundError problem when deploying in the OSGi container. To fix this issue, we need to have a clean repository which, thanks to this process, we can now detect (I was mentioning error reporting added previously).
- The only change this process is really doing is adding header manifests to the jar file, the content of the jar file itself is not modified. If the jar file was a signed jar file, then changing the manifest breaks the overall signature even if you do not touch any of the headers containing the signature of individual classes. To fix this issue, in our case it is ok to simply remove the signature entirely.
- This process does not fix libraries that are simply not OSGi compatible. For example, OSGi do not support classes in the default package which for example the jdom library exposes, or they have class loading issues (famous Class.forName() OSGi issue). To fix this problem (which from my experience has been very rare), we have been using SpringSource versions.
The last point I wanted to raise is my concern that there isn’t a ‘one-size-fits-all’ repository. Even with the amazing work that SpringSource is doing with the free repository, you get their interpretation of dependencies. For example, the jdom bundle (version 1.0) has the following entry: Import-Package: org.jaxen;version=”[1.1.1, 2.0.0)”;resolution:=”optional”
The above entry basically means that jdom depends optionally on org.jaxen package version 1.1.1 all the way to 2.0.0 not included. This may work for you or not depending on your needs. In our case we like tighter version ranges ("[1.1.1, 1.1.2)"). What if jaxen v.1.2.3 ends up having a show-stopper bug when used in conjunction with jdom but you still need it for other parts of your code and you end up deploying it in the same container ? Stay tuned for a separate post entirely dedicated to version management soon.
Also, check out our series on OSGi at LinkedIn.
Tags: osgi, yanpujante
trackback
http://blog.linkedin.com/2009/02/17/osgi-at-linkedin-bundle-repositories/trackback/



OSGi at Linkedin - Bundle Repositories « The OSGi Look February 19th, 2009
[...] Read the complete article on the Linkedin Blog [...]
Jeason February 20th, 2009
This is my first visit.Wish to visit this blog frequently.
Valery Abu-Eid February 27th, 2009
Hello Yan,
When I’ve read your post last week I was in my final stages working on Bundler:
http://www.dynamicjava.org/projects/bundler
I’ve been tackling problems of integrating the OSGi-incompliant libraries with OSGi for the past 7-8 month and I reached to the conclusion that it can be statistically proved that repositories and manual work can not hold against automated bundle generation. I will post an article about this next week.
I developed Bundler to make sure that I wouldn’t waste a single minute of my time on integrating any OSGi-incompliant library/framework, the approach was tested against more than 100 OSGi-incompliant JAR archives. You can consider using/trying it to reduce development time. It’s just that some of the cases handled by Bundler can eliminate days of work to run frameworks like JAX-WS RI and JAX-RS RI with different OSGi Frameworks in different Java Runtime Environments.
Best Regards,
Valery
Paul Krause March 3rd, 2009
Interesting post, Yan. Thank you.
I am curious why didn’t choose to build the OSGi headers as part of your maven build. It seems like a natural fit.
Care to elaborate?
Yan Pujante March 10th, 2009
Paul,
We do not use maven to build. We use ant/ivy. We integrated the generation of the OSGi headers as part of the ant/ivy process by calling custom ant tasks to generate the jar file (= bundle) which inderneath uses bnd.
Yan
Sagnik March 22nd, 2009
thank you for this wonderful article.
Actually, I am completely naive in this field of osgi. I am trying to make some osgi bundle out of jar files. could you please little bit elaborate the process or give me any link to some article etc.
Mario Sundar March 25th, 2009
Responding on behalf of @Yan
Mario from LinkedIn
The myth of software reuse | All the small things April 1st, 2009
[...] beautiful dream rather than an actual fact. If you look at well known companies adapting OSGi like LinkedIn, you’ll see what I mean. Where are we heading to when one is forced to define dependencies [...]
The Quest for Software Reuse | All the small things April 23rd, 2009
[...] a detailed guideline on doing so (neither does Java). This is the reason, why the the folks from LinkedIn choose to only rely on one distinct version in their dependencies – the safest call for sure. If [...]
Heji Kim November 19th, 2009
Yan,
Thanks for sharing. I’m very curious as to your OSGi progress since you have not posted in 9 months. Are you near rollout?
Thanks,
Heji