Eclipse Extension Points und Extensions

Eingetragen bei: Eclipse, RCP | 1

Eclipse beinhaltet ein hoch modulares Konzept um Erweiterungen Plugin-übergreifend realisieren zu können. Das zu erweiternde Plugin bietet einen Extension-Point an, an dem andere Plugins mit ihren Extensions andocken können. Beide Arten an Plugins müssen hierbei zwangsläufig im Manifest als singleton definiert sein.

Um einen Extension-Point zu definieren, wird die plugin.xml Datei des zu erweiternden Bundles geöffnet und zum Reiter Extension Points gewechselt. Sofern keine plugin.xml Datei existiert, kann alternativ im Manifest (MANIFEST.MF) der genannte Reiter aktiviert werden, Eclipse erstellt nun automatisch die plugin.xml Datei. Nun kann ein Extension-Point erzeugt werden, als ID und Namen erhält er im folgenden Beispiel den Wert de.mz.myExtensionPoint und myExtensionPoint.


Nach erfolgreicher Erstellung des Extension-Points wird automatisch das dazugehörige Schema geöffnet. Auch wenn es nicht zwingend notwendig ist, sollten alle Attribute im Schema – Reiter Definition – definiert werden. Nur so können die Felder beim Anlegen einer Erweiterung bequem im Plug-In Manifest Editor über die UI angegeben werden können. Will man später Veränderungen am Schema vornehmen, so ist dieses in der Dateistruktur des Projektes unter schema/myExtensionPoint.exsd zu finden.

Hier wird nun ein neues Element mit dem Namen client erzeugt, dem zwei Attribute zugewiesen werden. Das Erste Attribut hat den Namen class und ist vom Typ Java. Hier hat man nun die Möglichkeit ein Interface zu hinterlegen, welches von der im Attribut class verwiesenen Klasse zu implementieren ist. Im folgenden Beispiel ist das zu implementierende Interface IMyExtension.

Das Interface selber enthält für Beispielzwecke die Methode doSomething:

public interface IMyExtension {
public void doSomething(String text);
}

Das zweite Attribut erhält den Namen text und ist vom Typ String. Hier sind keine weiteren Einstellungen nötig.

Per Context-Menü wird der extension zunächst ein Choice-Element zugewiesen, welches wiederum den Eintrag client erhält. Damit die Extension beliebig viele Client-Einträge erhalten kann, wird die Max Occurrences des Client-Elements auf Unbounded gesetzt. Eine Extension kann also mehrere Erweiterungen enthalten.

Die Plugin.xml hat nun folgenden Eintrag erhalten:

<extension-point id=“de.mz.myExtensionPoint“ name=“myExtensionPoint“ schema=“schema/myExtensionPoint.exsd“/>

Damit andere Bundles nun tatsächlich den Extension-Point erweitern können, müssen sie das Interface IMyExtension implementieren können. Daher muss im Manifest das Package des Interfaces exportiert werden:

Export-Package: de.mz

Damit ist die Definition des Extension-Points abgeschlossen und wir können uns der Erstellung einer Extensions zuwenden. Öffnen wir hierzu die plugin.xml Datei des Bundles, welches den Extension-Point erweitern soll. Auf dem Reiter Extensions kann zwischen den möglichen Erweiterungen gewählt werden, auch der zuvor erstelle Extension-Point de.mz.myExtensionPoint wird hier aufgeführt. Per Context-Menü können hier die Client-Erweiterungen hinzugefügt werden. Für die im Schema deklarierten Attribute stehen im Plug-In Manifest Editor Eingabehilfen zur Verfügung, so kann bequem für das class Attribut eine Implementierung und für das text Attribut ein String angegeben werden.

Diese Einstellung führt zum folgenden Eintrag in der plugin.xml Datei:

<extension point=“de.mz.myExtensionPoint“>
<client
class=“de.mz.MyExtensionImpl“
text=“Hallo Welt“>
</client>
</extension>

Extension-Point und eine dazu passende Extension wurden bereits erstellt, nur Verwendung findet die Erweiterungen bislang noch nicht. Doch hierzu bietet Eclipse die ExtensionRegistry an, auf die man über die Runtime-Platform Zugriff erhält:

IConfigurationElement[] extensions = Platform.getExtensionRegistry().getConfigurationElementsFor(„de.mz.extension.example.myExtensionPoint“);
for (IConfigurationElement extension : extensions) {
try {
String text = extension.getAttribute(„text“);
IMyExtension myExtension = (IMyExtension) extension.createExecutableExtension(„class“);
myExtension.doSomething(text);
// do with myExtension what you have to do
} catch (CoreException e) {
// do some logging
}
}

In obigem Beispiel erhält man alle Erweiterungen die zum Extension-Point de.mz.extension.example.myExtensionPoint gehören. Nun können die Attribute der einzelnen Erweiterungen erfragt werden – hierbei ist es nicht relevant, ob diese im Schema definiert sind – und für das Klassen-Attribut ein entsprechendes Objekt erstellen werden (createExecutableExtension). Bei einem optionalen Klassen-Feld macht es Sinn, sich zunächst zu vergewissern, dass das Attribut einen Wert zugewiesen bekommen hat. Andernfalls wird nämlich eine CoreException geworfen, die über diese Abfrage verhindert werden kann.

Auch wenn das Beispiel auf die Eclipse 3.x Plattform ausgelegt ist, funktioniert der Mechanismus grundsätzlich auch bei Eclipse 4. Hier spart man sich den Umweg über die Runtime-Platform, da sich die ExtensionRegistry bequem per Dependency Injection injizieren lässt.

Will man das Beispiel testen, kann das Laden der Extensions in einem Activator vorgenommen werden, der hierfür lediglich im Manifest eingetragen werden muss:

Bundle-Activator: de.mz.Activator

Nun kann das Extension-Point Bundle als OSGI Framework ausgeführt werden. Treten hierbei Fehler auf, sollten in der Run-Configuration zunächst alle Bundles deselektiert und anschließend nur die Plugins ausgewählt werden, die den Extension-Point und die Extensions beinhalten. Natürlich werden auch Eclipse spezifische Bundles benötigt, die sich über einen Klick auf Add Required Bundles hinzufügen lassen.