Plugins, Extensions, and Extension Points

Understanding the concepts of extension points and extensions is fundamental to understanding the JLense platform. Extension points and extensions together are the most basic form of reuse and assembly in the JLense framework.

Extension points are well-defined places in the system where other tools (called plug-ins) can contribute functionality. An extension is a specific contribution to an extension point. A plug-in is a structured component that describes itself to the system using a manifest(plugin.xml) file. Plug-ins can define their own extension points in thier manifest file and other other plug-ins may contribute extensions to those extension-points.

The platform core includes a runtime engine that starts the platform base and dynamically discovers plug-ins. The platform maintains a registry of installed plug-ins, thier extension-points, and all contributed extensions.

Each major subsystem in the platform is itself structured as a set of plug-ins that implement some key functionality and that define thier own set of extension points. Plug-ins can define their own extension points or simply add extensions to the extension points of other plug-ins.

Most platform subsystems add extension points to the platform and provide APIs for extending their functionality. Some of these subsystems supply additional class libraries that do not directly relate to an extension point, but can be used to implement extensions. For example, the workbench UI supplies the UIWorks UI framework.

Defining an extension point

When you want to allow other plug-ins to extend or customize the functionality of your plug-in, you should define an extension point. As with extensions, the first step in defining an extension point is to add some markup to the plugin.xml file of your plug-in. Here is an example:

   <?xml version="1.0" ?>
   <plugin
      name="Simple Extension Point Plugin"
      id="org.jlense.sample"
      version="1.0">
      <runtime>
         <library name="extpt.jar" />
      </runtime>
      <extension-point
         name="Sample Extension Point"
         id="sampleExtensionPoint"/>
   </plugin>

That's all you have to do to define your own extension point. The structure of the extensions that connect to this extension point are not interpreted by the platform, but instead should follow a schema defined by the extension point provider. The only restriction is that the extension markup must be valid XML. Once you have defined your extension point, you can query the platform's plug-in registry at runtime to query and process any extensions that may exist. At runtime, the extensions are manifested as IExtension objects. Extensions in turn are made up of a tree of IConfigurationElement objects, one for each element in the extension markup. The following code snippet queries the plug-in registry for any extensions that are associated with the extension point we defined above.

   IPluginRegistry registry = Platform.getPluginRegistry();
   IExtensionPoint point = 
    registry.getExtensionPoint("org.jlense.sample.sampleExtensionPoint");
   if (point != null) {
      IExtension[] extensions = point.getExtensions();
      System.out.println("Found " + extensions.length + " extensions");
   }

In simple cases, the only interaction between an extension point and its extensions is contained in the extension markup. The extension may provide some data or preference setting directly within the XML of the extension markup, and the extension point plug-in then reads and processes that information to provide some customized behaviour.

Interacting with code in an extension

With more advanced extension points, you may want to interact at runtime with objects defined by the extension. The trick here is that the plug-in that defines the extension point will typically know nothing about the names of classes and packages in the extension, so it must define an interface to describe the class structure it expects. Then, it can instantiate classes in the extension's plugin using an IExecutableExtension, and use the interface to interact with the object at runtime.

To illustrate with an example, the following snippet from a plugin.xml file defines an extension that hooks into our sample extension point defined earlier.

   <extension
      name="Sample Extension"
      id="myExtension"
      point="org.jlense.sample.sampleExtensionPoint">
         <someObject>
            <run class="org.jlense.examples.SomeClass"/>
         </someObject>
   </extension>

This extension includes markup for an executable extension, which includes the name of the class to be instantiated. This class should conform to an interface defined by the plug-in that defined the extension point. As described earlier, the names of the tags in this example ("someObject" and "run") are arbitrary. These tags will be defined by the extension point schema.

For this example, say the interface for the executable extension is called ISomeInterface. The following code snippet in the extension point's plugin will query the plugin registry, and instantiate all executable extensions for the extension point "sampleExtensionPoint".

   IPluginRegistry registry = Platform.getPluginRegistry();
   IExtensionPoint point = 
    registry.getExtensionPoint("org.jlense.sample.sampleExtensionPoint");
   IExtension[] extensions = point.getExtensions();
   for (int i = 0; i < extensions.length; i++) {
      IConfigurationElement[] elements = extensions[i].getConfigurationElements();
      for (int j = 0; j < elements.length; j++) {
         if ("someObject".equals(elements[j].getName())) {
            ISomeInterface object = 
                (ISomeInterface)elements[j].createExecutableExtension("run");
            System.out.println("Found an executable extension: " + object);
         }
      }
   }

That's all there is to it! For more information, browse the javadoc for the org.eclipse.core.runtime package. The best example of all is to look at the extension points defined by the JLense platform itself.