OSGi, Dynamics and Eclipse

I often hear this question…

“Why does Eclipse prompt me to restart if it’s a dynamic OSGi-based application?”

Eclipse Restart Dialog

As a user, you’re presented with a few choices: no, apply changes (I’m feeling lucky) or yes (please restart).

To answer this question, let me tell you a story. Back in the day, Eclipse had its own module system… things that you see these days in the MANIFEST.MF file were all in the plugin.xml file:

Eclipse 2.0 Plug-in

These old plug-ins were lazy, but not dynamic in the OSGi sense. New plug-ins could come, but nothing would ever go away. In the the PDE team, we like to use the analogy of a play. So imagine a play where all the actors come out for their parts… but they never leave the stage. With the adoption of OSGi, Eclipse was able to go fully dynamic and actors can now leave the stage (and come back again!).

However, Eclipse still had to support these old plug-ins even though we moved our runtime to OSGi. How we did this is another story, but it involves translating the old plugin.xml files to OSGi manifest files on the fly.

Since we supported the old plug-in model, we had to prompt for restart. I consider this the historical reason of why the restart dialog exists. Now let me tell you the more serious reason.

The practical reason is that most people don’t code for dynamics still. Even though the Eclipse RCP stack is fully dynamic aware there may be pieces out there that aren’t. Whether it’s Eclipse code or someone else’s code, it doesn’t matter. It just takes one bundle that isn’t dynamic aware to potentially mess things up. To illustrate what it means to be dynamic aware or not, let’s look at some real code from Eclipse.

/**
 * Manages argument selectors (choosers) for string variables.
 *
 * @since 3.0
 */
public class StringVariablePresentationManager {

	/**
	 * String variable presentation extension point identifier
	 * (value "stringVariablePresentations").
	 *
	 * @since 3.0
	 */
	public static final String EXTENSION_POINT_STRING_VARIABLE_PRESENTATIONS =
            "stringVariablePresentations"; //$NON-NLS-1$

	// default manager
	private static StringVariablePresentationManager fgManager;

	// extension point attributes
	public static final String ATTR_NAME = "variableName"; //$NON-NLS-1$
	public static final String ATTR_ARGUMENT_SELECTOR = "argumentSelector"; //$NON-NLS-1$

	/**
	 * Table of configuration elements for variable presentations,
	 * keyed by variable name.
	 */
	private Map fConfigurations;

	/**
	 * Returns the singleton string variable presentation manager.
	 *
	 * @return the singleton string variable presentation manager
	 */
	public static StringVariablePresentationManager getDefault() {
		if (fgManager == null) {
			fgManager = new StringVariablePresentationManager();
		}
		return fgManager;
	}

	/**
	 * Returns an argument selector contributed for the given
	 * variable, or null if none.
	 *
	 * @param variable string substitution variable
	 * @return argument selector or null
	 */
	public IArgumentSelector getArgumentSelector(IStringVariable variable) {
		IConfigurationElement element =
                   (IConfigurationElement) fConfigurations.get(variable.getName());
		if (element != null) {
			try {
				return (IArgumentSelector)element.createExecutableExtension(
                                   ATTR_ARGUMENT_SELECTOR);
			} catch (CoreException e) {
				DebugUIPlugin.log(e);
			}
		}
		return null;
	}

	/**
	 * Constructs the manager, loading extensions.
	 */
	private StringVariablePresentationManager() {
		initialize();
	}

	/**
	 * Load extensions
	 */
	private void initialize() {
		fConfigurations = new HashMap();
		IExtensionPoint point= Platform.getExtensionRegistry().getExtensionPoint(
                   DebugUIPlugin.getUniqueIdentifier(),
                   EXTENSION_POINT_STRING_VARIABLE_PRESENTATIONS);
		IConfigurationElement elements[]= point.getConfigurationElements();
		for (int i = 0; i < elements.length; i++) {
			IConfigurationElement element = elements[i];
			String name= element.getAttribute(ATTR_NAME);
			if (name == null) {
				continue;
			}
			fConfigurations.put(name, element);
		}
	}
}

Can you tell what’s potentially wrong with the above code?

Extensions are only read when the object is initialized. The code above doesn’t handle extensions in a dynamic fashion so if new bundles come and provide more string variables, the code above won’t see them. Now imagine this pattern applied to some user interface code where you could contribute menu entries. If the manager was already initialized and your bundle providing entries came along at a later time, you wouldn’t see your menu entry contribution until the system restarted.

How do we make this code dynamic aware? Well, let’s look at another real example from Eclipse:

public final class KeywordRegistry implements IExtensionChangeHandler {

	private static final String ATT_ID = "id"; //$NON-NLS-1$
	private static final String ATT_LABEL = "label"; //$NON-NLS-1$
	private static KeywordRegistry instance;
	private static final String TAG_KEYWORD = "keyword"; //$NON-NLS-1$

	/**
	 * Return the singleton instance of the KeywordRegistry.
	 *
	 * @return the singleton registry
	 */
	public static KeywordRegistry getInstance() {
		if (instance == null) {
			instance = new KeywordRegistry();
		}

		return instance;
	}

	/**
	 * Map of id->labels.
	 */
	private Map internalKeywordMap = new HashMap();

	/**
	 * Private constructor.
	 */
	private KeywordRegistry() {
		IExtensionTracker tracker = PlatformUI.getWorkbench().getExtensionTracker();
                tracker.registerHandler(this,
                     ExtensionTracker.createExtensionPointFilter(getExtensionPointFilter()));
		IExtension[] extensions = getExtensionPointFilter().getExtensions();
		for (int i = 0; i < extensions.length; i++) {
			addExtension(PlatformUI.getWorkbench().getExtensionTracker(),
					extensions[i]);
		}
	}

	public void addExtension(IExtensionTracker tracker, IExtension extension) {
		IConfigurationElement[] elements = extension.getConfigurationElements();
		for (int i = 0; i < elements.length; i++) {
			if (elements[i].getName().equals(TAG_KEYWORD)) {
				String name = elements[i].getAttribute(ATT_LABEL);
				String id = elements[i].getAttribute(ATT_ID);
				internalKeywordMap.put(id, name);
				PlatformUI.getWorkbench().getExtensionTracker().registerObject(
						extension, id, IExtensionTracker.REF_WEAK);
			}
		}
	}

	private IExtensionPoint getExtensionPointFilter() {
		return Platform.getExtensionRegistry().getExtensionPoint(
				PlatformUI.PLUGIN_ID, IWorkbenchRegistryConstants.PL_KEYWORDS);
	}

	/**
	 * Return the label associated with the given keyword.
	 *
	 * @param id the keyword id
	 * @return the label or null
	 */
	public String getKeywordLabel(String id) {
		return (String) internalKeywordMap.get(id);
	}

	public void removeExtension(IExtension extension, Object[] objects) {
		for (int i = 0; i < objects.length; i++) {
			if (objects[i] instanceof String) {
				internalKeywordMap.remove(objects[i]);
			}
		}
	}
}

The above code is dynamic aware via IExtensionChangeHandler, notice the addExtension(...) and removeExtension(...) methods. These methods handle extensions coming and going which we would expect in a fully dynamic system.

Note, this dynamics problem applies to any OSGi system… not just Eclipse. Any OSGi system where your bundles come and go that may or may not be dynamic aware can cause problems.

How do we solve this problem? Well, the first step would be to know whether a bundle that we’re going to install and activate is dynamic aware. At the moment we have no idea. I could imagine a solution where there could be a new header: Dynamic-Aware: true

This header would give OSGi provisioning systems some metadata to make a intelligent decision whether a restart would be required or not. For now, in my opinion, the safest decision is always to prompt to restart and leave the decision in the hands of the user. In the future, I hope that we can shed this with improved education and provisioning metadata.

In summary, the problem of why Eclipse prompts you to restart is two-fold: there’s a historical reason due to the old plug-in model but there’s also the other problem of educating people how to write dynamic aware bundles. In the end, it only takes one bad bundle to ruin the dynamics of your OSGi system.

3 Responses to “OSGi, Dynamics and Eclipse”

  1. A.Sokolowski says:

    Now, this is the explanation! At least I understand the reasons for the dialog, and will not complain about it any more :)
    But still, the massage could say more clearly, or rather say the same thing in the way, that makes an user to feel more comfortable, something like that: “If you are not sure what to select, just restart Eclipse”.

  2. Philipp Kursawe says:

    That StringVariablePresentationManager screams for a refactoring patch then.
    Regarding extension I wonder what the best approach should be: Save the extensions in an internal state (a map like in your second example). Or query the available extensions on demand. Especially when they just provide key-value pairs I see no reason to duplicate that functionality and memory in a own state variable. I think the extension registry is optimized for performance already, so string lookups into the IConfigurationElement should be fairly quick?
    The LTK refactoring participants are also not dynamic aware, btw.
    I have written myself an ExtensionPoint class which allows to quickly visit all available extensions and filter them and then work with them. Also I have my own ExtensionRegistry base class that is essentially a tracker as you show in your second sample, and it calls (via reflection) methods on the subclass to actually handle incoming and going extensions like this (for your second sample):

    @Required({“id”, “label”)}
    protected void addKeyword(IConfigurationElement) {
    }

    As you can see, my ExtensionRegistry class also verifies that the extension has the required attributes. Which could be extended to add specific constraints to the attributes, such as a regex or type constraint and range constraints.

    Overall programming dynamic aware is not that difficult at all. It just take a little more extra effort but the gain is great.

  3. Ralf Ebert says:

    Chris, are you aware of any plans to add such a “Dynamic-Aware” manifest header? Seems like a reasonable addition and would certainly reward people that are sticking to the rules regarding OSGi’s dynamic nature.

3 responses so far

Written by . Published in Categories: Planet Eclipse

Looking for a job?

X
Karlsruhe / Remote
JavaScript
Mobile
Karlsruhe / Victoria / Remote
Windows
Mobile
Karlsruhe / Victoria / Remote
Java
Android
Mobile