Monday, July 9, 2007

Best Practice with Depressed Bundles

After an active life, some bundles feel that it has been enough and just want it to end. They carefully get their Bundle object and slowly calls uninstall... Can this be done in an OSGi Framework?

The issue is that the stop() method returns to a method of a bundle that has just been stopped. This means that its Bundle Context is no longer valid, its services have been unregistered and its Class Loader released. Any attempt to use any of the OSGi mechanisms will therefore result in an exception. For this reason, stopping oneself has always been frowned upon in the OSGi inner circles. However, being able to end itself is useful sometimes. We briefly touched this subject in R4 but did not take the proper time so we deferred it to the next release. Well, now is the time.

The proposed solution was to allow a bundle call an asynchronous stop/uninstall/update method. However after a heavy and sometimes violent discussion we basically concluded that stopping oneself or stopping another bundle both have exactly the same issue. If a bundle is stopped by another bundle, there is always a possibility that the stopped bundle is not stopping correctly and continue to execute after being stopped. From a class loading point of view, this can not do much harm, class loaders are only garbage collected when they are no longer used. As long as a class is on the run stack of some thread, it can not be garbage collected. It is only when OSGi mechanisms are used that additional problems occur. Any programmer that stops its bundles, should (obviously) not continue to execute code because its stop method has been called, that is the contract.

We therefore decided to not add an additional mechanism and leave the spec as is.

However, what is the best practice in this area? The best practice is to execute the stop/update/uninstall method in a separate thread:

void stopSafe(final Bundle bundle ) {
new Thread() {
public void run() {
bundle.stop();
}
}.start();
}

Or do you have other ideas?

Peter Kriens

P.S. I have added a snippet as well that shows how it all fits together

3 comments:

  1. Now I am confused: how does that make sure I am not executing any code after the thread.start() call? And wouldn't that code pose the same problem?

    ReplyDelete
  2. I agree it is not a clean solution but I really do not know a better one. The garbage collection model make this code work but if the programmer did more things after the start() call, he would be screwed as well. Nasty problem.

    ReplyDelete
  3. I think the term "execute code" is quite confusing and probably a bit of a strong statement in this context. The only thing stopping a bundle does is call the BundleActivator stop method and invalidate the BundleContext. If a bundle does not behave properly with respect to cleaning up services/resources/threads etc. then it does not matter if it stops itself or another bundle stops it. In both cases it may have bad behavior.

    Classes continue to be able to be loaded (from exported packages and using Bundle.loadClass method) and code continues to be able to execute. The only way to prevent loaded code from executing in OSGi is to either uninstall or somehow force the bundle to be unresolved followed by a PackageAdmin.refreshPackages call to clean out the bundle classloader.

    ReplyDelete