OSGI and Extension Point Best Practices for createExecutableExtension

So you are a user interface guy and you simply want to use an extension point declared by a systems developer (possibly an Eclipse platform developer or more likely your own in house platform team) and when you run you get an error that states your bundle can not load a class that is contained in your bundle. Here we define a bundled named com.ibm.rcp.junit.myplugin.badbundle and we are attemping to use an extension point declared in another bundle, let’s call that bundle com.ibm.extension.provider.

In this case, the com.ibm.extension.provider bundle is supplying its own log redirector so the basic system outs of core Eclipse will be stored in another file. Here is what is logged in your custom log file:

org.eclipse.core.runtime.CoreException: Plug-in com.ibm.rcp.junit.myplugin.badbundle was unable to load class com.ibm.rcp.junit.myplugin.badbundle.BadAction

then you get:

org.eclipse.core.runtime.CoreException[1]: java.lang.ClassNotFoundException: com.ibm.rcp.junit.myplugin.badbundle.BadAction


The problem with these errors (from the com.ibm.extension.provider plugin) is the first one states our bundle can not load its own class! And then it states the com.ibm.extension.provider is throwing a ClassNotFoundException implying a pathing problem for our plugin (com.ibm.rcp.junit.myplugin.badbundle) to the not so OSGI knowledgable developer.

So let’s try to explain what happened.

In this example our plugin is actually throwing an exception in the start() method of its bundle (a NullPointerException to be exact) and these errors above are printed to the log of the base plugin who is attempting to load all of its extensions for its extension point by calling createExecutableExtension(). Since this looks like a “platform” bug we need to get on the horn and ask why com.ibm.extension.provider can not load our class com.ibm.rcp.junit.badbundle.BadAction.

The developer responds with a “hey, fix your plugin! You need to be running JUnit tests on your plugin to see if it loads correctly. Or better yet, launch your plugin with the -console, do the “ss” command to get your bundle ID and run the command “start ” to get the load error.

This may be true, but it still does not hide the fact that the logging is completely misleading to the real problem which is “our plugin is not loading because our start() method is throwing an exception“.

So who needs to change? Both of us!

The extension provider plugin should be capturing these explicit errors around createExecutableExtension() and we really should be unit testing our plugin with JUnit test and using the OSGI console to ensure our plugin can load. By catching the CoreException and then looking explicitly for the secondary throw of ClassNotFoundException the com.ibm.extension.provider could print a more comprehensive message like this:

WARNING: Error loading Extension Provider Action: com.ibm.rcp.junit.myplugin.badbundle.BadAction
Can not load class(com.ibm.rcp.junit.myplugin.badbundle.BadAction) from bundle(com.ibm.rcp.junit.myplugin.badbundle).
Check the bundle start() method or the bundle exports to make sure the bundle is loading and the class is exported properly.

Then print the same stack traces above. If you are using a log redirector for your plugins you might also want to attempt starting the bundle with code like this:

Bundle bundle = Platform.getBundle(pluginID);
try {

if (bundle != null)
bundle.start();

} catch (BundleException e1) {

logger.log(Level.WARNING, getString(“str.myplugin.bundle.start.error”), e1);

}

This will log the exact error in the consumers plugin (our extension plugin) and pinpoint the exact line the NullPointerException is taking place. Here is the actual error:

org.osgi.framework.BundleException: Exception in com.ibm.rcp.junit.myplugin.badbundle.Activator.start() of bundle com.ibm.rcp.junit.myplugin.badbundle.
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:1013)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:969)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:256)
at com.ibm.extension.provider.EPPlugin.processActions(EPPlugin.java:329)
at com.ibm.extension.provider.EPPlugin.init(EPPlugin.java:193)
at com.ibm.extension.provider.EPPlugin.access$2(EPPlugin.java:159)
at com.ibm.extension.provider.EPPlugin$initJob.run(EPPlugin.java:538)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58)
Caused by: java.lang.NullPointerException
at com.ibm.rcp.junit.myplugin.badbundle.Activator.start(Activator.java:36)

This log entry now points back to our plugin with the exact line where the NullPointerException is taking place!

Best Practices

  • Extension point providers should capture createExecutableExtension() and log what’s really going on with their extension loading
  • Extension providers (us in this case) really should be unit testing our bundles with JUnit or at least attempting to explicitly load the bundle on the OSGI console.
  • You really should limit code in the start() method of your bundles for many reasons
  • You should never call createExecutableExtension from the BundleActivator start() method. Doing so could cause ClassCircularityErrors to occur.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.