When first diving into OSGi, you probably don’t notice fragments at all. After a while, once you become familiar with the concept and the main parts of the spec, you start looking into ways to accomplish things, which were fairly simple done in plain Java, but might be really painful in OSGi. By then the latest, you’ll find them in the spec and they are really tempting in so many ways. Before I elaborate on that, let me just summarize the main features of fragments in order to give everyone a chance to follow.
Fragments, like bundles are just simple jar files with additional manifest headers; actually they are almost the same as the bundle manifest headers. At first glance, bundles and fragments look alike. Both are jars by definition and specify additional manifest headers. The huge difference is that a Fragment is like virus. It needs to be attached to a host in order to function. During the attaching process of a fragment to a exactly specified host, the manifest files were logically merged if not conflicting and the dependencies are being resolved, if possible (if not possible, the fragment will just not be resolved and attached). After that, the resources in the fragments are loadable with the host ClassLoader, thus the Fragment does NEVER have its own ClassLoader (but its own ProtectionDomain if not an extension fragment, but we are not going there). Actually, there are quiet a few more things worth mentioning, but to understand what I am training to explain here, it should be fine. If something beyond that is needed, I am going to explain it as we go.
Now, that we know, what a Fragment is, what is it actually for where is the problem. Well, the purpose as described in the spec is pretty straight forward and logic. For customization of your bundle depending on the environment, often only very little, yet delicate changes are sometimes necessary. This can be a simple localization issue of your translations or the packaging of system dependant libraries like “dll”, “.so” or “.dmg” you are invoking in your bundle code. Depending on system properties like “os.name” or “lang” you can let the container decide, which bundles/fragments suit your environment – so far so good.
Now, where might be the problem with that? To motivate this, one has to keep in mind that one of the strengths of OSGi is its encapsulation model. Based on this declarative, strong and rigid component definition, OSGi became a very robust deployment format/ environment. A bundle or component is very expressive about its requirements and features, which makes it easy to handle them. Now, with Fragments attached to a host, the behavior of the host might change, without any changes of the version information within the bundle. The former strong formalism is now softened. Especially if you look into Software Product Lines, where you have thousands of bundles in many different versions, dynamically wired together, you can’t test every combination effectively; you have to be sure, that you can rely on a certain behavior, enforced by the component contract, which is in this case no longer possible.
The question is how a fragment potentially manages to violate the component contract. Concerning this question, there has been a discussion on the OSGi-Dev list this week. Just imagine you have a bundle X with three packages a, b and c. Fragment F defines one Import-Package: b. Another Bundle Z provides the package b as an export and has c internally available (see figure).
We further assume that bundle X defines in package a only a BundleActivater loading a class from package b. Now we have everything set, to begin our small example. Starting the bundle X (with the attached fragment F) will result in loading the BundleActivator. The activator then will try to load the class from package b. Depending on the OSGi class loading strategy, the class loader first looks into the import statements of the host, if there are no entries (like in our case), the fragment is queried for import statements. This time we’re successful and the ClassLoader is trying to obtain the class from another bundle. Here we have our problem. The behavior of X can be altered by providing another implementation of package b as an export of bundle Z in the container (it has to be a different bundle – import your exports in the fragment won’t work in that case). The problem here is not, that the package is replaced with an alternate implementation; this is pretty common in OSGi with the best practice of importing your exports. The crux here is that there is no statement of imports and the actual developer of the bundle might not have intended to replace the package at all.
Of course this behavior has advantages as well. Consider you have a OSGi-efied bundle, which unfortunately isn’t converted perfectly, so you still have some Class.forName() calls in one minor package. The license however doesn’t allow you to touch the bundle at all, but now with the help of a fragment, you can inject your own code as described above. Not sure, if one really needs that, but you can.
Something else you can do with fragments is to emulate a global class space. I was actually working in on a project, where we had a pretty monolithic software in terms of the core DTOs. Although “Extensions” were easy exchangeable and loadable with different class loaders, the core data transfer objects were not, unless you serialize them, which of course was out of discussion. Now imagine, you have one core bundle, where every extension contributes its DTOs in form of a fragment, you end up with a global class space for all your DTOs. Nice on first glace, it becomes nasty soon, when you think about the implications. First of all, versioning is now only very limited available. Second, every new deployed fragment lets your whole application refresh, because everything depends on this core bundle and every fragment is attached to it – at least for me, no desirable solution.
The finish this (actually pretty long) post today, I can think of one last case, where it can help. Imagine, you have multiple versions of a package available and your bundle requires a distinct version, because of strange constraints, but can’t make these imports explicit, but dynamic with DynamicImport-Package, normally you don’t have a chance to provide a distinct version. With a fragment, you are able to inject a distinct version before the DynamicImport-Package definition is used in the class loading strategy.
However, I tried to show different ways of using fragments, besides the usual ones of providing translations or system dependant libraries. Everyone can of course do what ever he or she wants, but in my opinion, all these alternative usages are extremely dangerous and I can’t think of any sane reason to use them in real projects. Just because you can do something doesn’t mean it is go to do so. I gave it quiet a thought and I actually came to the conclusion, that the class loading behavior should be changed in a way that, no matter what happens, the bundle class path lookup should be performed entirely, before any attached fragment is asked. This would be a valid solution to prevent violations of the bundle contract in my opinion, but well… these are just my 2 cents. Take it or leave it
–
Mirko