If you ever developed an Eclipse plug-in, chances are you had to implement an “extension” to plug your functionality into the Workbench. In fact, Extensions and Extension Points lie at the core of Eclipse extensibility and customizability; plug-ins that provide higher-order services typically expose their extension or customization points to other plug-ins by defining one or more Extension Points in their manifests.
Listing 1: Custom View implementation declared as an Extension.
<extension point="org.eclipse.ui.views"> <view class="com.developer.examples.ExampleView" id="com.developer.examples.view1" name="Example View"> </view> </extension>
An Extension Point is essentially a specification of a programming interface that plug-ins can use to provide their own implementation of some service, function, or behavior to the declaring plug-in. They do this in turn by declaring one or more Extensions to that Extension Point in their own manifests (plugin.xml). Each Extension Point defines its own “content model” using an XML Schema document, with which the corresponding Extensions must comply. Extensions typically consist of one or more elements with attributes that represent some properties, including names of classes that implement some public interfaces.
How Dynamic Is Your Plug-in?
Since version 3.0, the Eclipse runtime was redesigned around the OSGi model; each plug-in is an OSGi bundle and the entire application consists of many inter-dependent bundles that, through mutual cooperation, deliver the overall application functionality. Such applications can be very dynamic—it is theoretically possible to install and uninstall bundles at runtime without ever stopping the whole application.
In practice, bundle implementers must ensure that their bundles will not cause errors when they’re uninstalled, or if any other bundles that they reference are uninstalled. They also should be aware of any new bundles that are installed during their operation. To that end, plug-ins may be “dynamic-aware,” “dynamic-enabled,” or both (or neither).
Dynamic-enabled plug-ins must be sure to clean up after themselves when uninstalled (technically, when they’re being “stopped”). For example, they must deregister themselves as listeners of any other plug-in that they registered with, release any UI resources, and so forth.
A dynamic-aware plug-in correctly responds to other plug-ins being installed and/or uninstalled. For example, if it holds on to any references to an extension whose plug-in is uninstalled, it must release this extension. Conversely, if it discovers that new extensions have been registered, it must make use of them appropriately (for example, instantiate them, and so on).
Tracking Plug-In Extensions
Listing 2: Plug-in code for using IExtensionTracker to track Extensions.
public void start(BundleContext context) throws Exception { IExtensionRegistry reg = Platform.getExtensionRegistry(); IExtensionPoint ep = reg.getExtensionPoint(MY_EXTENSION_POINT_ID); tracker = new ExtensionTracker(reg); IFilter filter = ExtensionTracker.createExtensionPointFilter(ep); tracker.registerHandler(this, filter); IExtension[] extensions = ep.getExtensions(); for (int i = 0; i < extensions.length; ++i) addExtension(tracker, extensions[i]); } public void stop(BundleContext context) throws Exception { if (tracker != null) { tracker.close(); tracker = null; } } public void addExtension(IExtensionTracker tracker, IExtension extension) { IConfigurationElement[] configs = extension.getConfigurationElements(); for (int i = 0; i < configs.length; ++i) { // use configuration properties for something // ... MyDelegate delegate = (MyDelegate) configs[i].createExecutableExtension(CLASS_ATTR); // do what you need to do // ... delegates.add(delegate); // register association between object and extension // with the tracker tracker.registerObject(extension, delegate, IExtensionTracker.REF_WEAK); } } public void removeExtension(IExtension extension, Object[] objects) { // stop using objects associated with // the removed extension for (int i = 0; i < objects.length; ++i) delegates.remove(objects[i]); }
To achieve dynamic awareness, a plug-in registers itself with the platform’s IExtensionRegistry as an IRegistryChangeListener. This way, it is made aware of any changes to the extension registry and has a chance to respond appropriately. For example, if an extension is added to one of the plug-in’s own extension points, it can make use of the new extension. Likewise, if an extension is removed, it can release any references to it. How to keep track of what objects are associated with which extensions is up to the plug-in’s implementer.
In Eclipse 3.1, the IExtensionTracker interface and a default implementation of it, ExtensionTracker, have been been added to help plug-ins be dynamic-aware without having to repeatedly implement the same extension tracking code. To track UI extensions, a plug-in obtains the appropriate IExtensionTracker from the Platform. For example, a Workbench-wide extension tracker can be obtained through IWorkbench.getExtensionTracker(). Likewise, they also can be obtained for IWorkbenchWindow and IWorkbenchPage. If need be, the plug-in can instantiate and maintain its own ExtensionTracker.
To make use of an IExtensionTracker, the plug-in first registers its own IExtensionChangeHandler. The tracker then calls the appropriate method on the handler whenever a relevant extension is added or removed. So far, this sounds a lot like the extension registry change listener. However, the extension tracker also helps with tracking associations between objects and extensions. When an extension is added, the handler’s addExtension(IExtensionTracker, IExtension) method is called. The handler can respond by creating any objects for the added extension and register them with the tracker by calling its registerObject(IExtension, Object, int) method. The object can be tracked as a strong, soft, or weak reference. Whenever the extension is removed, the handler’s removeExtension(IExtension, Object[]) method is called, with an array objects that were previously associated with the given extension (through the tracker’s registerObject method). At this point, the handler can perform any clean-up related to these objects.
If it needs to, the plug-in can remove any object/extension associations from the tracker by calling one of its removeObject methods even before the extension is removed from the registry.