Legacy code in OSGi has always been a problem. OSGi has such a dynamic nature, most libraries are either not aware of the potential errors that can be imposed by suddenly disappearing bundles or just use techniques not suitable for the OSGi programming model, like absolute paths, usage of file references instead of URLs, context class loaders and others alike. Despite these, I would call generally bad programing practices, which can be avoided even in non OSGi environments (maybe except the context class loader problem), there is one thing which is typical for regular applications and just doesn’t quite work within OSGi – static, implementation based APIs.
So, what do I mean with that. In short, the common APIs you are usually faced with when dealing with plain Java applications. You have a class which represents your API, you may have some configuration files to set your system up and that’s it – plain simple. Logging is a perfect example for that. Let’s take any of your preferred, non OSGi logging APIs. You have a configuration file or system properties, which set the basic configuration and then you are good to go. Just call a static factory, obtain you logging object and start what ever you wanna do with it.
Now, what is the problem with this in an OSGi context? At first glance, nothing. You can set-up your system and it behaves like in plain Java. Problems only occur when you start trying to use it in a dynamic, typical OSGi-way. Changing a configuration during run-time, providing the configuration in a different bundle or fragment will require you to take more things like the start order into account. This in return makes your bundles no longer following core principles of OSGi, which say that as soon as a bundle gets resolved, it can be used.
Solving this seems hard. You can’t change a public API. Not like usual, just extract interfaces and make the implementation available as a service. Well, in some cases this might be possible, sure it won’t be in all cases. Looking at this issue, what is the core problem? The export of the API packages! Assuming, you only depend on packages (not using Require-Bundle) bundles, depending on a certain API become resolved, as soon as this API becomes available as an exported package.
A possible solution to this dilemma is treating export packages like services. During run-time, a bundle can explicitly add or remove exported packages. With this, you would be able to only export a certain API when you are certain, it will work as expected. Of course, this needs more thinking, because this imposes several problems (Security is just one of them), but the potential is great. To be honest, I am currently not sure if it is worth the risk or even the effort, but I am definitely sure it is worth exploring. Just think of the possibility to actually eliminate the dynamic import header with all of its drawbacks and removing proprietary workarounds like buddy class loading. So, what do you think?