OSGI: How to handle the (wrong) bundles startup order

Context

In some situations it is needed that some of the OSGI bundles are started in a specific order.

A concrete case is when the Apache Camel is used in the context of the OSGI. If one of the OSGI bundles are an user defined Apache Camel component and another bundle uses this user defined Camel component into a (Camel) route then the bundle containing the Camel component should be started before the bundle that is using the Camel component.

Solution – modify the bundle/s start level

The first solution would be to modify the start level of the bundle that you want to start later.

Apache Felix offers the “bundlelevel” command:

bundlelevel - set bundle start level or initial bundle start level
scope.
flags:
-i, --setinitial set the initial bundle start level
-s, --setlevel set the bundle's start level

So something like:

 bundlelevel -s newStartLevel bundleId

will do the trick.

The advantage  of this solution is that you do not need any programming skills to do it and you can apply it on any bundle (even on the bundles that you are not controlling the content).

The drawback of this solution is that is totally manual (at least in case of Apache Felix server).

The OSGI specification also  defines the OSGI Start Level API which provides the following functions:

  • Controls the beginning start level of the OSGi Framework.
  • Is used to modify the active start level of the Framework.
  • Can be used to assign a specific start level to a bundle.
  • Can set the initial start level for newly installed bundles.

Using the OSGI Start Level API it is possible to programmatically set the start level:

Bundle bundle = framework.getBundleContext().installBundle(location);
BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
bundleStartLevel.setStartLevel(xxx);

Solution – use a BundleListener

The basic idea is that the bundle B (than needs the bundle A to be active) will wait until the the bundle A is marked as started. This can be achieved by implementing a BundleListener to the level of bundle B.

The implementation of the “bundleChanged” method of the listener will look like this:

public void bundleChanged(BundleEvent bundleEvent) {
    String symbolicName = bundleEvent.getBundle().getSymbolicName();
    int eventType = bundleEvent.getType();

    if ("The Bundle A Symbolic Name".equals(symbolicName)
        && BundleEvent.STARTED == eventType) {
        //here we know that bundle A is started
        //so can do something that will need
        //bundle A
     }
}

The advantage of this approach is that the bundle developer is in control of the behavior. On the the other side this approach will not work if you do not own the code of the bundle that you want to start later.