• Share this article:

Does the body rule the mind, or does the mind rule the body?

Friday, May 19, 2006 - 20:29 by Wayne Beaton

In comments to a recent blog entry of mine, beche-de-mer (which I believe translates to “sea-slug”), asked if I could post an example of using extension points in an Eclipse RCP application that uses Swing. I decided to oblige with this hastily cobbled together example.

I added an extension point to my Eclipse RCP Swing example (the “root” plug-in displayed below). This extension point invites other plug-ins to contribute buttons to the window that “root” creates. Other plug-ins, like plug-in “A” and “B” displayed below, contribute extensions that include a label to display on the button and the name of a class to execute when the button is clicked. To keep things simple, I decided that the class must extend java.lang.Runnable.

The implementation is pretty straightforward… The “root” plug-in adds the following extension point declaration to its plugin.xml:

<plugin>
<extension-point id="button" name="button" schema="schema/button.exsd"/>
...
</plugin>

Then, I declared the following extension in the plugin.xml for plug-in “A”:

<plugin>
<extension point="org.eclipse.examples.swing.rcp.button">
<button
action="org.eclipse.examples.swing.rcp.addon.Ouch"
label="Hit me"/>
</extension>
</plugin>

As I mentioned, this extension point defines the name of the Runnable to execute when the button is clicked (“action”) and the text display on the button itself (“label”).

Finally, I changed the application code to the following:

public class Application implements IPlatformRunnable {
public Object run(Object args) throws Exception {
JFrame frame = buildWindow();
while (frame.isVisible()) Thread.sleep(1000);
return IPlatformRunnable.EXIT_OK;
}

private JFrame buildWindow() {
JFrame frame = new JFrame("Eclipse RCP");
frame.setLayout(new GridLayout(2,1));
frame.setLocation(50, 50);
frame.add(new JLabel("Eclipse RCP application with Swing!"));
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
addButtons(panel);
frame.add(panel);
frame.pack();
frame.setVisible(true);
return frame;
}

private void addButtons(JPanel panel) {
IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint("org.eclipse.examples.swing.rcp.button");
for (IExtension extension : point.getExtensions()) {
for (IConfigurationElement element : extension.getConfigurationElements()) {
if ("button".equals(element.getName())) {
panel.add(makeButton(element));
}
}
}
}

private JButton makeButton(final IConfigurationElement element) {
String label = element.getAttribute("label");
JButton button = new JButton(label);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object object = null;
try {
object = element.createExecutableExtension("action");
} catch (CoreException e1) {
e1.printStackTrace();
}
if (object instanceof Runnable) {
Runnable runnable = (Runnable)object;
runnable.run();
}
}
});
return button;
}
}

The addButtons() method does most of the work. It obtains the extension point and then the extensions. It iterates through the extensions and then delegates to the makeButton() method to actually create a button (with an appropriate label and action listener).

The final product looks something like this:


Both of the push buttons come from extensions (there is a second extension defined). This “application” is now very easy to enhance by simply adding more extensions in one or more plug-ins (a single plug-in can define multiples extensions for the same extension point). Only the buttons contributed by the plug-ins I actually include at runtime will end up on the window.

This example isn’t exactly industrial strength. I almost immediately discovered that using Runnable as the “action” class is insufficient. It’d probably be better to create a custom interface that provides some kind of application state in a parameter so that the action can actually do something interesting. Also, I don’t handle the CoreException very well (it should be logged at least). But I think the example gets the point across.

I’ve put the code into CVS. You can get it from server/repository: dev.eclipse.org/cvsroot/org.eclipse, directory: /www/evangelism/samples/swing/code. There’re two plug-in projects there. Enjoy.

Note that I’ve updated the CVS information.