After some less OSGi technology centric posts I am now getting back to the meat and will highlight the problems involved with dynamic imports. In well designed OSGi applications the “DynamicImport-Package” header should be obsolete. Unfortunately not every code is OSGi aware. Actually it is not the missing OSGi awareness that is the core of the problem. It is more the notion of making wrong assumptions on the visibility of classes and the misuse of context class loaders in 90% of the cases I saw. But what is the problem in a concrete example?
Well, a common example is JAXB. You have a library/API bundle which for itself provides no real functionality, but needs to be “enhanced”. You need actual implementations to provide functionality. Unfortunately your library needs access to those classes. OSGi is very strict with its class path definition. This is done in a static manner and has to be set during build time of your bundle (Import-Package header in the manifest), but your library provider has no idea about the implementation bundles (and it shouldn’t!). As you can see there is no way of defining such dependencies at built-time. In OSGi the only way to enable this without modification of the bundle code is to include a dynamic import header.
Eclipse soon realized that this is an issue. Especially the undirected import forces in many cases a binding to bundles that are not really related to the bundle defining the import. This causes issues with restarts and updates of the library bundle, because a lot, if not all bundles, can be affect by that. Even worse, it can mean that you end up finding the wrong classes (and I am not yet talking about versions). Eclipse, while migrating to OSGi had a huge amount of legacy code not aware of OSGi concepts and so they were the first really feeling the pain. That’s why the Eclipse community introduced the concept of buddy class loading. Although a better way by providing a scope (you have to define that you’re a contributor on the one side and declare that you’re open for that on the other side), it doesn’t solve the problem of different versions or provider selection in general.
Quiet a few people assume this is the only way to work around this kind of problems and they are correct when you don’t have the possibility of adding you’re own mechanisms or the API prevents other measures. However, if you have the access to the source, it is likely that you actually can do something that solves the problem.
While working on my thesis, some years ago, I came across this problem and in some cases you are able to use basic OSGi mechanisms to get what you need. The core idea and the requirement is, that the library needs the implementation class and offers a way to either inject the object or tweak the class loading mechanism to actually find the class you are looking for. So the question is how to gain access to the implementation object or its class loader for that matter. For the ones familiar with OSGi this should be obvious. We instantiate the implementation in our implementation bundle and offer that one as a service. We can register that object with the interface(s) theses classes are implementing or the annotation class they got annotated with. This is an universal key to identify the correct service. The nice thing about this is also that OSGi will handle the version problem for free. With Spring or the up coming RFC 124 we don’t even have to write a single line of code in most cases, we don’t even have to touch the bundle. All we need is to provide is a fragment attached to the implementation bundle with a spring configuration inside creating a bean and publishing that one as a service.
Ok, we now have our implementation objects flying around in the ServiceRegistry. The next step is providing access to those objects in our library bundle. This is the actual tricky part and the point where you’ll most likely will face problems. Your API needs to provide a way of injecting these service objects and should allow you to dynamically manage them (add/remove/update). Otherwise you’ll end up with a static solution that once started won’t be able to adapt to the dynamic nature of OSGi. If you’re API provides/ uses a class loader look up that can be tweaked you’re the lucky one. Here you can provide a delegating class loader that dynamically queries the ServiceRegistry for the fitting object and returns its class loader. This is what we did in our OSGi-efied UIMA implementation. The drawback here is, once you provided the defining class loader you lost control on how to resolve others unless your library has some mechanisms for that, which is unfortunately not very likely.
As you’ve seen, you not always have to go for a dynamic import. All the mentioned approaches have their advantages and disadvantages and none of them are “perfect”, but at least you have the choice choosing the one fitting best to the specific problem context.
If you found other ways of migrating problematic legacy code, please let me know. Maybe there is a solution out there, I was just not aware of. You never know