1. The patch may be large (it is the entire bundle that is shipped), even if only a single class changed.
2. Some other bundles may depend on a fixed version of the packages exported by the bundle that is being patched. Hence, we will have to ship new versions of the affected bundles as well, which can lead to a chain reaction. In the worst case, all the bundles may have to be shipped due to a change to a single class in a core bundle!
3. If the dependent bundles import packages in a range of versions, such as [1.0,2.0), the importer will be wired to the bundle which exports the package with the highest version. In this case, shipping a new version of the bundle may work, provided that backwards compatibility is satisfied. However, this may not be always possible.
We can provide a simple solution to this problem using 2 OSGi constructs; require-bundle & fragments. Figure 1 shows that the required bundles take precedence over the bundle's classpath. Hence we can use required bundles to override classes in the bundle.
Figure 1: Class loading flowchart. Source: OSGi R4 spec
However, at the time of shipping a bundle, we cannot know what patch bundles will be required. Hence we cannot put a Require-Bundle manifest header pointing to the patch, in the original bundle. This is where fragments come into play. The entries in the fragments' manifest are merged into that of the fragment host. Hence, we can create a fragment which will require the patch bundle. In effect we have managed to dynamically add a Require-Bundle header to the original host bundle. However, we should ensure that the original bundle is the primary exporter of the patched packages. This can be done by specifying a mandatory attribute. We need to add this to the Export-Package header of the fragment;
e.g. Export-Package:*;partial=true;mandatory:=partial
Once the fragment is attached to the host & the host is refreshed, we will be able to see the functionality in the patch taking effect.
If you ever require to revert the patch, it is just a matter of removing the fragment & patch bundles, and refreshing the main bundle.
I have created a PoC project which demonstrates this concept. You can download the code and try it out. The main bundle is the bundle that needs to be patched. The patch bundle is the required bundle & the fragment bundle attaches to its host, the main bundle. The Knopflerfish desktop tool can be used to test this out.
NOTE: Don't confuse this with bytecode level patching at runtime. That is a different aspect. I've written another post on how that can be done.
e.g. Export-Package:*;partial=true;mandatory:=partial
Once the fragment is attached to the host & the host is refreshed, we will be able to see the functionality in the patch taking effect.
If you ever require to revert the patch, it is just a matter of removing the fragment & patch bundles, and refreshing the main bundle.
I have created a PoC project which demonstrates this concept. You can download the code and try it out. The main bundle is the bundle that needs to be patched. The patch bundle is the required bundle & the fragment bundle attaches to its host, the main bundle. The Knopflerfish desktop tool can be used to test this out.
NOTE: Don't confuse this with bytecode level patching at runtime. That is a different aspect. I've written another post on how that can be done.





