This post is just a reflection, based on something that was recently discussed in the Bnd developer’s Slack group. It caused us to briefly touch on an age-old argument of
Historically, the Bnd team has been staunch supporters of favouring
Require-Bundle. However, the Eclipse team has not been so purist and the Eclipse core currently relies on
Require-Bundle in order to work properly. Even @pkriens admitted (in the aforementioned Bndtools Slack group) to softening his purist approach against
Require-Bundle (though I’m still not 100% sure if he was serious or joking… ) I was prompted to re-visit @pkriens blog post from a decade-and-a-half ago where the
Import-Package approach was put forward (OSGi Blog: JSR 277 and Import-Package).
I nearly always use
Import-Package for the reasons @pkriens outlined in the article. The only time I don’t is when my upstream dependencies are constructed in such a way as to force me to use
Require-Bundle, often because of split packages (which is the case with iDempiere). However, at the same time, I have also seen a glimmer of when
Require-Bundle might be a good idea (or at least, not a terrible idea).
In @pkriens’ original article, the argument is based on the concept of cohesion. Cohesion is roughly defined as the amount of interdependency between different parts of the code. If you draw a dependency graph, a highly cohesive block of code will have lots of links. To put it another way, there will lots of different parts of the code that don’t really work without each other (so it is rare to import one package without the others), and changes to one more often than not require changes to the others.
The argument consists of two parts:
- Versioning makes most sense on aggregates of code that are highly cohesive.
- Packages are usually highly cohesive, but bundles are usually not so cohesive.
I think that the first part of this argument is 100% accurate. However, upon reflection, I think that the second part of the argument, while generally true, is less absolute - and it is something that is under the control of the developer.
I think it is certainly true that packages tend to be more cohesive than bundles. I think it is equally certain that it is possible to produce packages that aren’t quite so cohesive - the fact that packages can be “split” across bundles, causing the “split package” problem, is an indication of a package whose cohesion is breaking up and should probably be refactored into separate packages.
However, it is possible to have a cohesive group that spans multiple packages. Examples are some of the API specs out there like JAX-WS or servlet APIs - groups of packages that implement a single version of the API. In such cases, it often makes sense to use a single version number for the group of public API packages. The OSGi even created the concept of “contracts” to encompass this very idea (disclaimer: I’ve never really used contracts in OSGi so this is based on my theoretical understanding). More on contracts here and here.
It is true that a bundle could theoretically contain packages that aren’t highly cohesive (indeed, in practice this is often the case). However, it is also possible that a developer could make a commitment to downstream users to be careful about how they aggregate (exported) packages into bundles - to maintain only and all cohesive packages in their bundle (ie, don’t move them out somewhere else without an appropriate major version bump, and don’t put other packages in there that aren’t closely related). If such a commitment is made, then the bundle becomes a de facto type of a “contract”. The advantage of using
Require-Bundle over a contract is that you don’t have to explicitly list all of the required packages that you will inevitably require if you are using the API.
The main disadvantage of
Require-Bundle is that you end up importing all packages in the bundle - including those that you don’t need. This increases dependency fanout unnecessarily. However, if the bundle you are requiring consists of only cohesive packages, chances are that you will end up needing to import all or most of them anyway, and as the packages are mutually cohesive they are likely to have more-or-less the same set of transitive dependencies so the chances of increasing your dependency fanout is small.
That being said, the only advantage I can think of here is that it’s easier to write your dependencies in your OSGi manifest - you only have to put one
Require-Bundle rather than several
Import-Packages. This advantage isn’t much of a real issue if you’re using Bnd to build your bundles (and automatically generate the appropriate
I’m not sure what the runtime performance overheads are of
Import-Package though. I can’t imagine there would be a great deal of measurable difference between the two.
So in the end, I think sticking with
Import-Package is probably the best way to go if you’re using the Bnd toolchain. However perhaps
Require-Bundle is not (always) as bad as it seems?