http://www.developer.com/

Back to article

Developing Eclipse Plug-ins


September 29, 2005

With the ever-increasing popularity of Eclipse as an IDE as well as the Rich Client Platform, many companies and individuals are getting into the business of developing Eclipse plug-ins. Have you ever wondered what it would take to write one yourself? This installment of my Eclipse series will take you through the process of developing a simple visual plug-in. This will give you an opportunity to learn about the fundamentals of Eclipse plug-in architecture and become familiar with the Plug-in Development Environment (PDE).

Introduction

Several years ago, when I first took a closer look at Eclipse (it was at version 2.0 at the time, as I recall), I was quite impressed by the fact that it was all available for free. It provided decent Java development capabilities, but nothing far above and beyond what was commercially available at the time. However, Eclipse really captured my interest when I discovered that I could easily extend its features with my own code. In fact, no privileged access or special tools were needed—Eclipse provided all tools and frameworks that were used in its own development. (Over the years I have come to believe that this recursive property is the hallmark of many clever frameworks and solutions in general).

Since then, Eclipse matured in all aspects—its design further improved, its architecture became more robust yet flexible, and its tools grew more powerful and effective. From what was initially a tool integration framework became a platform for arbitrary rich-client applications. However, the fact that the fundamental ideas behind Eclipse remain largely the same is in my mind a testament to the quality of its original design.

Extending Eclipse requires some basic understanding of its architecture and tools that are at your disposal. Because the easiest way to learn how to do something is to try doing it yourself, this article will help you develop a simple Eclipse plug-in using the tools provided by the Plug-in Development Environment. After getting a little background information on Eclipse architecture, you will use a PDE wizard to create a new plug-in, implement a simple workbench view, and test-run your work to see the results. The details of the implementation will be left for another installment. To accomplish this task, all you will need is a working installation of the latest Eclipse SDK (version 3.1). Visit http://www.eclipse.org/downloads to obtain a free copy.

Everything Is a Plug-in

From a developer's perspective, Eclipse is a platform for building a variety of client applications. While originally targeting development tools, the scope of Eclipse has widened to include ordinary rich-client applications, as well as non-desktop environments, such as hand-held devices and embedded systems. In order to support such a broad range of applications, a high degree of extensibility is necessary. Every Eclipse-based application consists of multiple components, called plug-ins, that interact with one another through a set of well-defined interfaces. Plug-ins are managed by a small run-time layer responsible for their discovery and activation. Starting with version 3.0, the original implementation of this layer was replaced by one based on the Open Services Gateway initiative (OSGi), thus improving Eclipse's portability across many hardware and operating system platforms and providing compatibility with a range of managed execution environments. As a result, Eclipse plug-ins are now implemented as OSGi bundles.

Similarly to other managed-component environments, the run-time characteristics of each Eclipse plug-in are specified in its descriptor, which is used by the run-time layer to configure the plug-in's execution environment. Prior to version 3.0, this descriptor was an XML-based file called plugin.xml, located at the root of the plug-in's directory. As a result of migrating to the OSGi run-time, plug-ins are typically packaged as JAR files and many of their properties, such as the unique identifier, version number, run-time classpath, and exported packages, are included in their JAR manifests (in other words, META-INF/MANIFEST.MF). However, a set of descriptor properties unique to Eclipse plug-ins—plug-in Extensions and Extension Points—remain in the plugin.xml file.

A plug-in's Extension Points are basically its component configuration points described using a subset of the XML Schema. A plug-in that implements a certain abstraction, such as displaying arbitrary views in the workbench, would publish an Extension Point specifying what each implementation needs to fulfill in order to be usable by this plug-in. These requirements typically include simple properties, such as window titles and icon paths, but also names of classes implementing specific interfaces. In this example, any plug-in wanting to provide a view implementation must do so by declaring an Extension of the Extension Point with ID org.eclipse.ui.views in its plug-in descriptor. This Extension must specify the name of a class implementing interface org.eclipse.ui.IViewPart. At run-time, the workbench plug-in searches the plug-in registry for all org.eclipse.ui.views Extensions and offers them to the user as displayable views. When the user chooses to open a particular view, the workbench plug-in instantiates the view's implementation class, specified in its plug-in descriptor, and interacts with it through interface org.eclipse.ui.IViewPart. This mechanism is the cornerstone of Eclipse's extensibility; plug-ins provide concrete implementations of other plug-ins' abstractions, and many in turn implement their own, higher-order abstractions and expose them as Extension Points for others to fulfill.

Listing 1: Contents of plugin.xml:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
   <extension
        point="org.eclipse.ui.views">
      <category
           id="com.developer.superview.category1"
            name="Developer.com"/>
      <view
           category="com.developer.superview.category1"
            class="com.developer.superview.Superview"
            id="com.developer.superview.view1"
            name="Superview"/>
   </extension>
</plugin>

The Plug-in Development Environment

As you can see, the core task of developing Eclipse applications essentially consists of creating plug-ins that implement one or more Extensions. Theoretically, there's nothing stopping you from using Notepad, or vi, and javac on the command-line to develop Eclipse plug-ins (but I want to hear from anyone who's actually tried!). Luckily, Eclipse comes with a robust set of tools that make this job much easier. The Plug-in Development Environment (PDE) is freely distributed as part of the Eclipse SDK, and serves as a good example of an Eclipse-based IDE tool—it is nothing more than a set of plug-ins with their Extensions (as a matter of fact, they also expose several Extension Points, which you may use to develop your own PDE extensions).

In PDE, each plug-in under development is represented by a single Java project. Plug-in projects have certain unique characteristics that make them what they are; for example, each has a plug-in descriptor (a manifest and/or a plugin.xml file), a dynamic classpath based on dependencies specified in the descriptor, special builders, and other configuration properties. PDE provides a plug-in creation wizard that creates plug-in projects populated with the required essentials.

PDE comes with a special multi-page editor that makes plug-in development easier. The Plug-in Manifest Editor actually spans three files—the bundle manifest (META-INF/MANIFEST.MF), plugin.xml, and build.properties. It allows you to edit all properties necessary to describe a plug-in—ts basic run-time requirements, dependencies, extensions, extension points, and so on. You will take a more detailed tour of this editor while developing the sample plug-in.

Because Eclipse plug-ins are essentially Java projects, they are built incrementally by default, and no special manual build steps are necessary. However, PDE makes it possible to create unattended builds using Ant; you can create an Ant build script for your plug-in (in Package Explorer, right-click your plugin.xml and choose PDE Tools -> Create Ant Build File), which provides targets for creating various build outputs (such as plug-in JARs, source archives, and so forth).

To test-run your work, PDE provides a special launch configuration type that allows you to launch another workbench instance (referred to as the run-time workbench) with your workspace plug-ins included in its configuration (this is also referred to as self-hosting—you don't have to install your plug-ins into an external workbench just to test-run them; instead, you can use your own development workbench installation, dynamically). Your Eclipse launch configuration will allow you to specify which plug-ins you want included, special command-line arguments, environment properties, and so on.

PDE also comes with a solid user documentation, as well as extensive reference information (Javadoc and Extension Point documentation). Because it is released under the same terms as Eclipse itself, its source code is freely available and in fact included by default with the SDK.

Creating a Simple Plug-in

To get a better idea of how to develop plug-ins using PDE, you will create a simple workbench view. Its purpose will be to list all views available in the workbench at run-time (thus, you will call it the Superview). While doing so, you will examine the various parts of the Plug-in Manifest Editor, using the example at hand.

To get started, launch your Eclipse SDK and choose an empty workspace. When you close the Welcome page, you should find yourself in the Java perspective, by default (however, it is not required that you are in the Java perspective in order to proceed). First, you will use the New Plug-in wizard to create the plug-in. Then, you will edit its descriptor using the Plug-in Manifest Editor, and implement a Java class using JDT. Lastly, you will launch our plug-in in a run-time workbench to see the results. Now, get started:

  1. In the main menu, click File -> New -> Project, then select Plug-in Project and click Next.
  2. On the next wizard page, enter com.developer.superview as the Project name. Click Next.
  3. On this page, leave all fields with their default values and just click Finish. Answer yes when asked whether to switch to the Plug-in Development perspective. At this point, the plug-in has been created and you should see the Overview page of the Plug-in Manifest Editor.

The Overview page contains some general properties, such as the ID of your plug-in, its version, name, provider, plug-in class, and platform filter. It also provides links to pages for specifying plug-in content, its build configuration, as well as links for test-running and exporting the plug-in (all with brief descriptions). The ID is required and must uniquely identify your plug-in, globally. The Version is typically a three- or four-part, dot-separated version identifier (major.minor.service{.qualifier}). Plug-in versioning is documented in detail in your Platform Plug-in Developer Guide; in this exercise, you will settle for the default 1.0.0. The Name and Provider are optional and are only used as display labels in product configuration. The Class property is also optional, and for now it'll suffice to say that it represents a singleton instance of the plug-in at run-time, typically used as a single point of access to various plug-in-specific data. In this example, the wizard generates a default implementation of this class for you, but you will not reference it from any code developed in this example. Platform filter can be used to restrict the plug-in's platform applicability (not used in this example).

The Dependencies page allows you to specify what other plug-ins yours depends on, for building as well as at run-time. Adding a plug-in dependency means that all Java packages that the chosen plug-in exports, as well as any Extension Points, are made available to your own code. You may choose to add any plug-in available in your SDK installation, as well as your workspace (in other words, other plug-ins you're currently working on). In this exercise, you have org.eclipse.core.runtime and org.eclipse.ui listed as your dependencies. Each dependency may be optional (just make sure your code is ready for it), and you may choose to re-export a dependency (select a plug-in in the list and click Properties...). You also may request that specific Java packages be made visible to your classloader, regardless of their origin. This is an advanced topic discussed in more detail in your Platform documentation. On this page, you may also perform various types of dependency analysis, which will give you a better idea of what exactly your plug-in depends on.

The Runtime page allows you to specify Java packages you want to export (in other words, make visible to plug-ins listing your plug-in among their own dependencies). In this exercise, you don't have anything to export because you don't provide any functionality that you intend to make available as public API. If you did, you could also qualify their visibility with respect to downstream plug-ins (also a more advanced topic). The Classpath section is used to specify which class folders or JAR files should be on your run-time classpath, if there are multiple (by default, only the root of your plug-in is included in its classpath).

The Extensions page is where you specify all your plug-in's Extensions. It consists of a tree of Extensions implemented by your plug-in (you can choose from the Extension Points published by plug-ins listed in your Dependencies), and a detail form with fields reflecting the current tree selection. To create new Extension elements, right-click an element in the tree and choose from the New sub-menu. The choice of elements, as well as each element's set of properties, depends on the associated Extension Point's schema. You can obtain your Extension Point's documentation by right-clicking the Extension in the tree, then choosing Show Description. In fact, to continue with our example, we will add a new Extension to provide our Superview implementation:

Figure 1: Plug-in Manifest Editor
Figure 1: PDE's Plug-in Manifest Editor
  1. Click Add. In the New Extension dialog, select extension point org.eclipse.ui.views. Click Finish.
  2. Right-click the Extension in the list; choose New -> category. Enter Developer.com as the name.
  3. Right-click the Extension again, choose New -> view. Enter Superview as the name and com.developer.superview.category1 as the category (to match the ID of the category created in the previous step).
  4. Click the class link. Enter Superview as the class name and click Finish. This will create a new class implementing the required interface and open it up in the Java Editor.

You will return to implementing the view in a moment. For now, activate the Plug-in Manifest Editor and click the Extension Points tab. If applicable, this is where you would list the Extension Points published by your plug-in. The details of each Extension Point schema is specified using the Extension Point Schema Editor (not discussed in this article).

The Build page allows you to specify your plug-in's build configuration (or check Custom Build for a completely custom build process, which you'd have to implement). This information is used by the incremental builder (your project's build path is updated accordingly, which is one reason you should not alter it directly, but let PDE do it for you), as well as an Ant build script, should you choose to generate one. In this exercise, you have no special build requirements. If you did, for example, use a custom icon for your view, then you'd have to ensure that its path is checked in the Binary Build section to make it accessible to your code at run-time.

The MANIFEST.MF, plugin.xml, and build.properties pages allow you to edit the source of each respective file. This is, however, not recommended for anyone but the more experienced Eclipse developers.

Now that you're familiar with the Plug-in Manifest Editor, you can continue developing your example:

  1. Activate the Superview.java editor. You'll see that, to complete the view implementation, you have to implement at least two methods: createPartControl(Composite) and setFocus(). At this time, I will not go into the details of the underlying API (JFace, in this case). For now, it should be enough for you to know that you create a table-like visual component—table viewer—to display your view's contents. You then configure it with a content provider to give it the ability to navigate your model (or more specifically, to extract your model's structural elements suitable for inclusion in a table), and a label provider, to give it the ability to convert your model's elements into table cells with textual labels and optional images. You also configure it with a sorter to display the entries in alphabetical order. Lastly, you give it an input—your model object, which is an array of descriptors of all views available in the workbench at run-time. The full implementation is shown in Listing 2.
Listing 2: Initial view implementation:

public class Superview extends ViewPart {

    private TableViewer viewer;

    public void createPartControl(Composite parent) {
        viewer = new TableViewer(parent);
        viewer.setContentProvider(new ArrayContentProvider());
        viewer.setLabelProvider(new LabelProvider());
        viewer.setSorter(new ViewerSorter());
        viewer.setInput(PlatformUI.getWorkbench().getViewRegistry().getViews());
    }

    public void setFocus() {
        viewer.getTable().setFocus();
    }
}

This gives you enough to run the example. Save all unsaved files and do the following:

  1. Right-click the plug-in project in your Package Explorer and choose Run As -> Eclipse Application. This will create a new Eclipse Launch Configuration with default values (which are sufficient for this example), and launch it.
  2. When the run-time workbench comes up, you should be greeted by the Welcome view. Click Window -> Show View -> Other... and select Developer.com -> Superview. Click OK.
Figure 2: Initial look of the Superview

What you should see is the Superview showing a list of entries in the form "View(<view id>)" (refer to Figure 2). This result is acceptable for the purpose of this exercise—you demonstrated the process of developing a simple visual plug-in using PDE, without going into too much API detail. However, if you want to delve deeper into JFace before the next installment of the series comes out, read on to see how you can make the view's contents more human-readable.

At this point, each entry in the table represents a workbench view; the model object you provided as the viewer's input is an array of view descriptors (IViewDescriptor), and the content provider you used turns any array object into a list of its elements (basically, given the array object as the parent, it returns its elements as the children). But, the label provider you installed simply uses each element's toString() method as its label. Interface IViewDescriptor does provide a method to get the view's label (getLabel()), and even the view's icon image (getImageDescriptor()). All you need to do to take advantage of them is override two of the label provider's methods:

  1. Close runtime workbench. In the Superview.java editor, subclass new LabelProvider() as shown in Listing 3:
Listing 3: Label provider customization:

viewer.setLabelProvider(new LabelProvider() {
  
    private final Map images = new HashMap();
  
    public String getText(Object element) {
        return ((IViewDescriptor) element).getLabel();
    }
  
    public Image getImage(Object element) {
        Image image = (Image) images.get(element);
        if (image == null) {
            IViewDescriptor view = (IViewDescriptor) element;
            ImageDescriptor desc = view.getImageDescriptor();
            image = desc.createImage();
            images.put(element, image);
        }
      
        return image;
    }
  
    public void dispose() {
        for (Iterator i = images.values().iterator(); i.hasNext();)
            ((Image) i.next()).dispose();
      
        super.dispose();
    }
});
Figure 3: Final Superview look
Figure 3: Final Superview look

Here, you've overridden the getText(Object) method to return the element's (an instance of IViewDescriptor) label as the text to show in the table. To make it more visually appealing, you also overrode the getImage(Object) method to return each view's associated icon image to accompany its label. This, however, is a little more complex. When you study JFace (the viewer framework used here) and SWT (its underlying widget toolkit), you will discover that images (org.eclipse.swt.graphics.Image) are operating system resources that need to be managed by the application. IViewDescriptor, however, only returns an ImageDescriptor rather than the Image itself. An image descriptor may be used to create an Image instance, but this must subsequently be disposed of when no longer in use. For that reason, you track all Image instances that you create in a descriptor-to-image map, and dispose them when the label provider itself is disposed (in other words, no longer needed by the viewer).

To see the final outcome, relaunch the run-time workbench (Ctrl+F11).

Packaging and Distributing Finished Plug-ins

Even though you may be finished with developing and testing your plug-in, you are still left with the task of getting it out of your development environment and into your users' hands. Luckily, Eclipse doesn't leave you to fend for yourself here either. At a minimum, you can use the Export wizard, easily accessible from your plug-in editor's Overview page. This will produce a distributable archive file, containing your plug-in's code and resources. The users can simply extract its contents into their Eclipse installation, and if all its dependencies are satisfied by their environment, Eclipse will discover and activate your plug-in as soon as its functionality is requested.

This kind of distribution and installation is called unmanaged—you leave the user with the responsibility of finding and installing any updates to your plug-in, should you release any in the future. A more structured approach is to group your plug-ins into features. A feature is a set of plug-ins that are installed and managed together. In addition to grouping the plug-ins, features contain information that allows the Eclipse Update Manager to locate any published updates and discover any new related features. Such updates are typically published in a special Web directory called an Update Site. PDE provides wizards and editors to support the development of features as well as update sites.

If you develop entire applications rather than just plug-ins (or features) that need an existing Eclipse-based product to install into, you would use the Product Configuration editor, together with the Eclipse Product export wizard, to package your application as a standalone Eclipse product. However, this is an advanced topic that deserves at least one article of its own.

Conclusion

Eclipse is a great tool and rich-client application platform not only for the free components it provides, but also for the openness of its own implementation. In Eclipse, all functionality is encapsulated in plug-ins, which "plug into" one another using well-defined extension points. Its Plug-in Development Environment provides powerful tools and wizards to help you get started developing your own plug-ins. More importantly, PDE will support your development throughout the entire cycle, including distribution and post-production maintenance.

In this article, you developed a simple visual plug-in to demonstrate the required steps and tools provided by Eclipse. In the next installment, you will take a closer look at the core components and frameworks used to build Eclipse applications.

Resources

  • Eclipse Help (click Help -> Help Contents in the main menu), also available online at http://help.eclipse.org/help31. See the PDE Guide section for information on PDE tools, and the Platform Plug-in Developer Guide for documentation on Eclipse architecture, API, and extension points.
  • Many good books, such as the Java Developer's Guide to Eclipse, Building Commercial-Quality Plug-ins, and Contributing to Eclipse.
  • The example plug-in developed in this article is available here. To import it into your clean, new workspace as a project, click File -> Import; then choose the Existing Projects into Workspace wizard, and specify the path to your downloaded archive file.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date