OSGi is used in many enterprise solutions and has proven to be very useful. However, once in a while you come across a scenario that is not properly addressed in OSGi or so you think.
Consider the following use case:
Application A in version 1.0.0 consists of a few bundles and they depend on code provided by library bundle L in version 1.0.0. This looks like a normal start of a new project, so nothing special here.
A 1.0.0 -> L 1.0.0
One of the promises of OSGi in contrast to JEE is that you can effectively share code among multiple applications in the same runtime, thereby saving PermGenSpace for classes and heap memory for loaded instances.
Using this mechanism, at a later point in time we introduce application B in version 1.0.0 that also depends on L 1.0.0
B 1.0.0 -> L 1.0.0
Again, nothing special here, since OSGi wiring allows for this scenario very elegantly. It should be noted here, though, that the introduction of B with its dependency on L has no side effect on A at all. Keep this in mind ![]()
The tricky part begins when we continue development of A by introducing new features and some bug fixes. A in version 1.1.0 is now newer and better than A 1.0.0, but it also requires a change (say a bug fix) in the common library bundle L, which demands a new version L 1.0.1.
A 1.1.0 -> L 1.0.1
The deployment of A in version 1.1.0 does not pose a problem for OSGi, even L 1.0.1 can be installed in parallel as the version of the bundle is different.
The question is now: What about application B?
If L 1.0.1 provides a bug fix, shouldn't B profit from the fix as well?
As a fan of OSGi the logical answer will certainly be: Yes, that would be great.
However, achieving this is easier said than done. In order for B to be wired to L 1.0.1 it would need to be refreshed. A simple restart of the framework doesn't do it, since the wiring is cached and persisted across restarts. Since an existing wire that satisfies the given constraints is preferred over a new wire, there is no automatism that would allow for B to be wired to L 1.0.1 just because it became available.
Now, if refreshing B is not so bad, then the discussion ends here. However, in enterprise scenarios, B could be running a very important business logic or even business transaction, which does not allow for a refresh at random times. Uptime of the framework was one of the selling points, but no customer cares about the uptime of the OSGi framework, what matters is the uptime of the application, B in this case. To solve this, there would need to be a time found at which the whole runtime is refreshed in order for the applications to be rewired to the newer version. You may also want to consider removing the old version of the bundle once it is no longer referenced.
Coming back to the question whether B should profit from the fix to L as well. If you really don't want B to be interrupted, the answer would need to be no. This demands some form of isolation between application A and B, since an innocent refresh on the runtime may very well cause the unwanted side effect of B getting wired to L 1.0.1.
This leaves you with a new metadata problem. Your application B may have stated its dependency on L using a version range of
[1,2)according to OSGi best practices, but some other higher level entity made the decision not to disrupt the application B at all. The only solution would be for A 1.1.0 to bundle L 1.0.1 in itself and not exporting the packages from L 1.0.1 to anyone else. While entirely logical and consequential, it puts a great burden on the maintainers of A (developers, deployers) and a lot of common knowledge about the side constraints of B, which A shouldn't really need to care about. Another side effect is that you no longer have a consistent class space in the framework such that any interaction between A and B that has worked before will now throw ClassCastExceptions as the classes are no longer compatible.
From previous discussions with Peter Kriens I learned that I can't have the cake and eat it too. Unfortunately, that is exactly what some customers want and they don't always care how we deliver this to them
Sounds familiar?
But, there is hope.
The CPEG is working on scoping solutions that allow for different application bundles to be completely isolated. There will be no interruptions in the lifecycle, and it will be possible to share classes among multiple applications. The composite bundle specification is still in the works, but it sounds like a life saver for a lot of enterprise scenarios. See RFC 138 if you have access to OSGi documents. All OSGi Alliance member companies do automatically regardless of membership level.
One of the drawbacks even with the composite bundle solution is that you still increase the memory footprint if you provide multiple versions of the same bundle(s) as described above. In that, OSGi is no better than any JEE application server.
However, there is one thing one can do to minimize the disruption. And that it to decouple interfaces from implementation and model dependencies based on services rather than implementations. It is quite logical that interfaces change much less frequently than implementations.
So, if the example from above was to look slightly different:
A 1.1.0 -> L 1.0.1, C 1.0.0 and B 1.0.0 -> C 1.0.0
then A could install as many fixes as it wants as long as it does not change the interface contract defined in C 1.0.0. Provided that B has now modeled a service dependency on C that A implements, B should not be interrupted significantly by the fact that A 1.0.0 is replaced with A 1.1.0. It's all in the metadata and proper dependency management ![]()
Hope this was illustrative about some of the challenges enterprises face in the real world.
As you were,
Tim.