Passing Data between Plug-ins
Abstract

The Eclipse Platform provides a very rich API for the development and configuration of plug-ins and RCPs. It does this in two ways: by providing the corresponding classes and interfaces or by using the extension point mechanism. During the development the question arises, how to develop own extension points and how those plug-in interfaces look like. This article summarizes some of my experiences with developing plug-ins extension points.
Introduction
The extensive use of extension points is the standard approach during the development of own plug-ins and Eclipse-based applications. Especially, in the 3.x branch, the Eclipse Developers introduced tons of new extension points, primarily for the user interface, moving towards the declarative definition of the UI. Even if the the topic of the definition of extension points is covered in several books and articles, it is a bit challenging to come up with a clean extension point design for a particular scenarios, especially for beginners. This has to do with the specific way, how the Eclipse platform handles extensions.
In order to have a concrete scenario, lets assume that a small RCP application consisting of two plug-ins is being developed. The application prints out the time every ten seconds. One plug-in is responsible for the functionality of the time generation (lets call it the Core plug-in) and another, for the presentation of the results of the first one (lets call it UI plug-in). The separation of code in UI and non-UI plug-ins is a common practice and the standard question is, how to to pass data between the two. In general we assume, that the UI plug-in depends on the Core plug-in.
In the following the different alternatives are discussed.
Call me synchronously
One of the most simple and common cases is the situation, in which the UI plug-in has the control and needs to call a method on some instance of the Core plug-in. This happens particularly in cases, in which a command handler or an action implemented in the UI plug-in is invoked on behalf of user interaction and the functionality of the Core plug-in is called. The only thing to do is to add the classes to the exported by the Core plug-in. Since the UI plug-in depends on Core, the classes will become visible (in terms of OSGi) and can be imported and used directly. If the method is synchronous, the result can be received upon the completion of the method and displayed by the UI plug-in. In this case the plug-in boundary disappears.
Call me asynchronously
The things get more complicated, if the call is not synchronous, but is invoked inside of a IWorkspaceRunnable or a Job. This situation is common in enterprise scenarios, when the invoked functionality is a long running operation, like loading data from a database or accessing a resource over the network. The signatures of the methods for those operations do not provide a possibility to get the invocation result.
public interface IWorkspaceRunnable {
public void run(IProgressMonitor monitor) throws CoreException;
}
public abstract class Job extends InternalJob implements IAdaptable {
protected abstract IStatus run(IProgressMonitor monitor);
}
In fact the method invocation of asynchronous calls can be performed from any plug-in (so not only UI) and it does not play any role for the result. Thus, this case can be discussed together with the case, in which the Core plug-in broadcasts the data change.
Don’t call us, we will call you
Another case, which can be seen as a general extension of the asynchronous case is if the state changes, and consequently the UI change originates from the Core plug-in. This use case is possible if there are several UI plug-ins working with one Core plug-in, and the changes inside of the Core plug-in have to be propagated to the views, following the well-known Model-View-Controller Design Pattern. Then, a common approach is the implementation of the Listener-Broadcaster Design Pattern. The UI plug-ins (Views) should register themselves as listeners by the Core plug-ins and will be notified on changes of its (Model) state. In this case the extension point mechanism of Eclipse can be used as discussed in the following. It is also a good idea, to hide the code dealing with Eclipse Extension Registry and extensions, so a simple implementation of the broadcaster is provided, too.
Definition of the extension point (Core plug-in side)
The definition of extension point for the listener/broadcaster case involves four steps:
- Creation of the listener interface
- Registration of the extension point
- Creation of the extension point schema
- Implementation of the broadcaster
The so defined extension point can be used by plug-ins, which then provide implementations of the listener interface. The broadcaster, located in the Core plug-in is responsible for sending the data to them. Note, that providing the implementation of the listener via extension point is logically equivalent to the creation of a factory which is responsible for the production of listeners. The Core plug-in has the control of how this factory is used, how many listeners (from each extension) are created, and when these are notified.
Creation of the listener interface
The interface for the listener is usually very simple. It provides a method, which is called by the broadcaster, e.G:
public interface ISimpleListener
{
public void eventOccured();
}
Sometimes the method takes parameters, like public void eventOccured(String message) or public void eventOccured(ISimpleEvent event) and sometimes it returns a value. In general, the signature of the notification method is application-specific and will not be discussed further. Since the implementers of the listener does not have control over the way, how and when this method is used, it is advised to add two life-cycle methods to the interface initialize() and terminate():
package de.techjava.rcp.plugins.core.listener;
/**
* A simple listener
* @author Simon Zambrovski, TechJava Group
*/
public interface ISimpleListener
{
/**
* Initialization life-cycle method
*/
public void initialize(Object source);
/**
* Signals the event occurrence
* @param event
*/
public void eventOccured(SimpleEvent event);
/**
* Termination life-cycle method
*/
public void terminate();
}
The life-cycle of the listener is then defined as follows: first, its parameter-less constructor is called, then the initialize method is invoked, providing the ability to hook into some infrastructure inside of the implementing plug-in. After the initialization, the eventOccured method is called multiple times. Finally, the listener is informed about the fact that the source will not send any information by the invocation of the terminate method. The source parameter of the ISimpleListener#initialize(Object) method again is application specific and should be chosen that way, that the listener is able to determine the source of events. Alternatively, the method ISimpleListener#eventOccured(SimpleEvent) can carry this information on every invocation, either in the SimpleEvent object or as additional parameter.
Registration of the extension point
After the Java interface of the listener has been created, the extension point can be defined. The easiest way to this, is to use the Plug-In Manifest Editor > Extension Points, but you can edit the plugin.xml directly. Basically, three values are required: id, name and schema location.

The corresponding plugin.xml code looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.5"?>
<plugin>
<extension-point
id="de.techjava.rcp.plugins.core.listener"
name="Simple Core Listener"
schema="schema/org.techjava.rcp.plugins.core.listener.exsd"/>
</plugin>
Since the dependent plug-ins should implement the ISimpleListener interface, the containing package has to be exposed. This can be done either by adding the package to the list of exported packages using the Plug-In Manifest Editor > Runtime > Exported Packages or by simple editing of the MANIFEST.MF file and adding the following line to it:
Export-Package: de.techjava.rcp.plugins.core.listener
Creation of the extension point schema
The definition of the extension point is performed by creation of the XML-Schema document, describing the structure and the data of the extension point. The Eclipse IDE provides a convenient Extension Point Schema Editor for this task, implemented as multi-page editor with master-detail page for schema definition. The creation wizard already creates the extension element. What has to be done is this:
- Create
New Element - Provide an adequate
Namefor it (listenerin our case) - Add an
Attributeto this element - Provide an adequate
Namefor it (classin our case) - Change the
Useto required - Change the
Typetojava - Fill the
Implementswith the full-qualified name of the listener interface (de.techjava.rcp.plugins.core.listener.ISimpleListener) - Optionally, fill the description
- Add a
Sequenceto theextension - Add a reference to the
listenerto theSequence - Optionally, adjust the multiplicity of the
listenerelement, to allow multiple listeners to be registered using one extension point
In the Extension Point Schema Editor the corresponding changes should look like this: 
Implementation of the broadcaster
After the interface is created and the corresponding extension point is defined, a broadcaster can be implemented. Its purpose is to hide the code related to the use of the Extension Point Registry. For convenience, the broadcaster can implement the same interface as the listeners do (ISimpleListener).
/**
* Simple broadcaster implementing the listener interface
* @author Simon Zambrovski, TechJava Group
*/
public class SimpleBroadcaster implements ISimpleListener
{
private static final String POINT_ID = "de.techjava.rcp.plugins.core.listener";
private static final String CLASS_ATTRIBUTE = "class";
private List<ISimpleListener> listeners = null;
/**
* Construct the broadcaster and initializes the listeners registered
* using {@link de.techjava.rcp.plugins.core.listener} extension
* point
*/
public SimpleBroadcaster(Object source)
{
// initialize on creation
initialize(source);
}
public void eventOccured(SimpleEvent event)
{
// broadcast the events
for (ISimpleListener listener : this.listeners)
{
if (listener != null)
{
listener.eventOccured(event);
}
}
}
public void initialize(Object source)
{
// retrieve the registered listeners
this.listeners = getRegisteredListeners();
// initialize
for (ISimpleListener listener : this.listeners)
{
if (listener != null)
{
listener.initialize(source);
}
}
}
public void terminate()
{
// terminate
for (ISimpleListener listener : this.listeners)
{
if (listener != null)
{
listener.terminate();
}
}
this.listeners = null;
}
...
}
The broadcaster holds the list of registered ISimpleListener instances. Its own implementation of the ISimpleListener is trivial and just delegates the calls to the elements of this list. The static method SimpleBroadcaster#getRegisteredListeners() is responsible for querying the registry for the registered extension and looks as follows:
/**
* Retrieves all registered listeners
*/
public static List<ISimpleListener> getRegisteredListeners()
{
IConfigurationElement[] decls = Platform.getExtensionRegistry().getConfigurationElementsFor(POINT_ID);
Vector<ISimpleListener> validExtensions = new Vector<ISimpleListener>();
for (int i = 0; i < decls.length; i++)
{
try
{
ISimpleListener extension = (ISimpleListener) decls[i].createExecutableExtension(CLASS_ATTRIBUTE);
validExtensions.add(extension);
} catch (CoreException e)
{
Activator.logError("Error instatiating the " + POINT_ID + " extension", e);
}
}
return validExtensions;
}
Use of the broadcaster
The use of the broadcaster from the Core plug-in is straight-forward. Here is an example of a job, which periodically sends message containing the time to the listeners.
/**
* Broadcasts the current time
* @author Simon Zambrovski
* @version $Id$
*/
public class SimpleTimeBroadcastJob extends Job
{
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private SimpleBroadcaster broadcaster = null;
public SimpleTimeBroadcastJob()
{
super("Time Broadcast Job");
}
protected IStatus run(IProgressMonitor monitor)
{
try
{
broadcaster = new SimpleBroadcaster(this);
broadcaster.eventOccured(new SimpleEvent("Curent time in Core is " + sdf.format(new Date())));
broadcaster.terminate();
return Status.OK_STATUS;
} finally
{
// restart again in a 10 seconds
schedule(10 * 1000);
}
}
}
Now, after the Core part is implemented, we focus on the implementation of the UI part, which receives the events.
Use of extension point (UI plug-in side)
The usage of the extension point is simpler than its definition and basically consists of the implementation of the listener interface and its registration. Here is a sample implementation writing to System.out:
public class SimpleListener implements ISimpleListener
{
private Object source;
public SimpleListener()
{
}
public void eventOccured(SimpleEvent event)
{
String message = "Event from " + source.toString() + ": " + event.getMessage();
System.out.println(message);
}
public void initialize(Object source)
{
this.source = source;
System.out.println("New " + SimpleListener.class.getName() + " listens to " + source.toString());
}
public void terminate()
{
this.source = null;
this.consoleStream = null;
System.out.println("Listners destroyed");
}
}
In order to tell the Eclipse Platform about the existence of this implementation, the extension point defined in the Core plug-in and has to be used in the UI plug-in. For this purpose, the UI plug-in must list the Core plug-in in its Dependencies and define the use of the extension point in the plugin.xml:
<extension
point="de.techjava.rcp.plugins.core.listener">
<listener
class="de.techjava.rcp.plugins.ui.SimpleListener">
</listener>
</extension>
That’s it.
Example
The example source for this article is available
here. It is a little bit more elaborate and provides a simple application, which prints the time into the Eclipse Console. I used Eclipse Galileo (3.5.0) – just copy the two projects into your workspace and run the product.

References
- Notes on the Eclipse Plug-in Architecture
- Design Patterns: Elements of Reusable Object-Oriented Software
5 Responses to “Passing Data between Plug-ins”
By Martin on Dec 7, 2009 | Reply
I don’t understand the step’Add a reference to the listener to the Sequence’ in the creation of the extension Point, could you check it again?
By Simon Zambrovski on Dec 7, 2009 | Reply
Read it like add a “reference to the listener” to the sequence. After execution of the previous step, your “extension” element should have a “sequence” element. If the “listener” element is already created, and you right-click on the “sequence” element, you will see the “new” popup-menu, containing “choice”, “sequence” and “listener” items. Clicking on the listener item, you will not create a new element called “listener” but a reference to existing one. If you look up the source of the definition, you’ll see something like: element ref=”listener”.
By Chris on Jan 25, 2010 | Reply
A great tutorial Simon.
I got it to work fine and without any issues. I just stumble over an issue right now. Lets say the UI is my main plugin (extends more than one extension point, lets say core1 and core2 – of different plugin’s each).
In your case the product launch configuration is in the core package – which works. However, if I try to create a product of the UI (with the core* plugins as dependencies and started) then it never seems to start the core* plugins… . Any hint?
By Simon Zambrovski on Jan 25, 2010 | Reply
There are several possibilities to fix this. For example the product configuration has the Launching configuration, where you could add the plug-ins to (and set up the activation during the start-up of OSGi). Another thing is: make sure to activate the checkbox “Launch plugin on access to the class”. The third thing is, make sure you design is correct.. It is important that your product / application is independent from the UI. Especially, it seems that you want to launch the Core* functionality and gather extensions implemented by the UI plugin. So, the right way to do so would be to trigger the core plugins from your Application.java… Again, I think you should seriously reason about the way your plug-ins are activated. Don’t rely on some order of OSGi activation, but make it explicit… The Application / Advisors allow you to participate in the lifecycle (check out the correspnding methods like preWindowsOpen() etc…). Make sure to insert your activation code there…
By Chris on Jan 25, 2010 | Reply
Many Thanks Simon, and again: great tutorial. I went with another tutorial first and it was seriously messed up. Yours is easy to understand!