Plugins

Aus Eclipse
Version vom 15. Juli 2010, 15:12 Uhr von Thies (Diskussion | Beiträge)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Ein Eclipse Plugin ist ein Equinox- bzw. ein OSGi-Bundle.

Die Struktur eines Plugins

Die Entwicklung eines Plugins kann in der Eclipse Entwicklungsumgebung erfolgen. Hierzu wird im Eclipse Workspace ein Plugin-Projektverzeichnis erzeugt, dem die Eclipse Entwicklungsumgebung standardmäßig folgende Struktur gibt:

Abb.5: Beispiel für den Inhalt eines Eclipse Plugin Projektverzeichnisses
  • .settings ist ein Eclipse spezifisches Verzeichnis, indem allgemeine Einstellungen bzgl. des Projektes persistiert werden.
  • bin ist das Standard-Ausgabeverzeichnis für dem Compiler. In diesem Verzeichnis werden die zu diesem Projekt gehörenden compilierten Dateien abgelegt.
  • icons ist ein standardmäßig von Eclipse bereitgestelltes Verzeichnis in dem Bilder, Icons ... abgelegt werden können.
  • META-INF enthält die für ein Plugin charakteristischen Metainformationen. Auf diese Datei wird weiter unten separat eingegangen.
  • im src Verzeichnis liegen die zu diesem Projekt gehörenden .java Dateien. Es handelt sich dabei um das Standard-Eingangsverzeichnis für den Compiler.
  • eine zusätzliche Konfigurationsdatei "build.properties" enthält Angaben zu Verzeichnissen und dem Ausgabeverzeichnis für den Compiler. Diese Datei ist für den Compiler relevant.
  • eine Datei für den classpath
  • eine Projektdatei


Die oben dargestellte Struktur kann vom Entwickler natürlich nach Bedarf abgeändert werden.


Im Eclipse Projekt-Explorer stellt sich ein Plugin-Projekt folgendermaßen dar:

Abb.6: Die Struktur eines Plugins im Project Explorer

Ein Eclipse Plugin besteht aus folgenden Elementen:

  • Jar Dateien, von denen der Plugincode abhängig ist
  • dem Sourcecode des Plugins (default ist das Verzeichnis \src)
  • einem Ausgabeverzeichnis für den Compiler (default ist das Verzeichnis \bin)
  • ein Verzeichnis META-INF
  • der Datei MANIFEST.MF (diese befindet sich immer im Verzeichnis META-INF)
  • einer .classpath Datei (diese Datei enthält den Projektclasspath)
  • einer .project Datei (diese Datei enthält die Projekteigenschaften)
  • einer build.properties Datei (diese Datei enthält die Angabe, wo sich das Sourcecode-Verzeichnis und das Compiler-Ausgabeverzeichnis befinden)
  • das plugin.xml ist eine optionale Datei, in der die Extension Points des Plugins definiert sind. Sind keine Extensionpoints Bestandteil des Plugins, so wird es keine plugin.xml Datei im Projekt geben. Die Plugin.xml wird im Kapitel Extensions noch weiter untersucht werden.
  • optional können weitere Verzeichnisse im Projekt enthalten sein (hier ist es das \icons Verzeichnis, in dem Icons abgelegt werden)




Es ist jederzeit möglich, ein Eclipse-Plugin in ein Standard OSGi-Plugin umzuwandeln. Dies geschieht, indem das Plugin-Projekt exportiert wird. Hiernach liegt ein normales OSGi Bundle in einem Jar Archiv vor, das unter jeder beliebigen OSGi Plattform lauffähig ist.

Abb.5: Beispiel eines in ein Jar exportierten Eclipse Plugins

Die in diesem Jar mit einem rotem Viereck markierte plugin.xml Datei ist allerdings eine für Equinox spezifische Datei. Es handelt sich hierbei um die Deklaration von Extensions und Extension-Points. Beide Konstrukte stellen eine Erweiterung der Equinox OSGi Plattform im Vergleich zu einer Standard OSGi Platform dar.

MANIFEST.MF

Abb.7: Beispiel für eine MANIFEST.MF Datei

Hier ein Beispiel für eine MANIFEST.MF:
Ein Plugin kann nichts 'von sich aus' initiieren. Es muss immer von außen 'angetriggert' werden, etwas zu tun (siehe auch das Hollywood Prinzip). Hierfür ist die Equinox- oder eine OSGi- Platform zuständig. Damit Equinox das Plugin nutzen kann, benötigt das Framework Informationen, die beschreiben, wie das Plugin zu nutzen ist. Diese Informationen sind in der MANIFEST.MF enthalten. Es handelt sich also um eine Beschreibung des Plugins, die durch ein OSGi Framework ausgewertet werden kann. Diese Datei wird bei der Installation eines Plugins geparsed, sein Inhalt wird in einer Struktur innerhalb des Frameworks abgelegt. Diese Vorgehensweise ermöglicht dem Framework Plugins erst dann zu instantiieren wenn sie benötigt werden ( s. Lebenszyklus).
Die MANIFEST.MF Datei besteht aus einer Liste mit Bundle-Headern. Jedem Bundle-Header werden ein oder mehrere Parameter - Werte Paare zugeordnet. Dabei können als Parameter sowohl Direktiven als auch Attribute angegeben werden.

  • Direktiven besitzen eine definierte Semantik für das Framework und werden mit := zugewiesen.
  • Attribute werden benutzt um Zuordnungen und Vergleiche zu realisieren. Ihnen werden Werte mit = zugewiesen.
  • Mehrere Parameter werden mit ; getrennt

Der Name eines Headers wird mit einem : von der zugehörigen Parameterliste getrennt.

Es folgt eine Beschreibung der Header der nebenstehenden MANIFEST.MF Datei:

  • Manifest-Version: Dieser Header ist Bestandteil der Jar File Spezifikation von Sun und spezifiziert die Version der Manifest Datei.
  • Bundle-ManifestVersion: definiert die OSGi Spezifikation nach deren Regeln dieses Manifest aufgebaut ist. 2 bedeutet, dass das Manifest den Regeln der OSGi Spezifikation Release 4 und später genügt.
  • Bundle-Name: legt einen menschenlesbaren Namen für das Plugin fest. Dieser Name soll das Plugin beschreiben und kann beliebig festgelegt werden.
  • Bundle-SymbolicName: Dieser Name ermöglicht zusammen mit der Version des Plugins die eindeutige Identifikation des Plugins im Framework.
  • Bundle-Version: Spezifiziert die Version eines Plugins .
  • Bundle-Activator: Der Name der Klasse die den Activator des Plugins darstellt.
  • Require-Bundle: Eine Liste von Plugins von denen das vorliegende Plugin abhängig ist.
  • Bundle-ActivationPolicy: bestimmt den Lebenszyklus eines Plugins. lazy bedeutet hier das das Plugins erst dann in den Status Active gesetzt wird, wenn auf eine Klasse des Plugins zugegriffen wird.
  • Bundle-RequiredExecutionEnvironment: ist eine Liste mit den Ausführungsumgebungen die im Framework vorliegen müssen.
  • Export-Package: eine Liste mit Packages die von diesem Plugin exportiert werden.
  • Import-Package: eine Liste mit Packages die von diesem Plugin importiert werden.
  • Bundle-Vendor: der Name des Herausgebers des Plugins. Dieser Name kann frei gewählt werden.




Eine vollständige Liste der Bundle-Header kann in der OSGi Spezifikation im Kapitel 3.2.1 nachgeschlagen werden.

Abb.8: Beispiel für den Manifest Editor in Eclipse

Die Eclipse Entwicklungsumgebung bietet einen komfortablen Editor an, um die MANIFEST.MF zu editieren.
Hier ein Beispiel des Editors für das oben abgebildete MANIFEST.MF: Der Editor ermöglicht auf den Tabulatoren Overview, Dependencies, Runtime, Extensions, und Extension-Points die Definition aller für ein Plugin benötigten Manifestheader. Der Editor erzeugt bei Bedarf auch andere deskriptive Dateien, wie z.B. die Plugin.xml. Die erzeugten Dateien können über weitere Tabulatoren am unterem Rand des Eingabebereichs in der Originalform angesehen werden.

Kapselung - oder die Sichtbarkeit auf Packages

Abb.9: Beispiel einer Manifestdatei mit Import-Packages und Export-Package Header

Wie schon weiter oben erwähnt, sind zunächst innerhalb eines Bundles nur die eigenen Packages zu sehen. Soll das Bundle auf weitere Packages zugreifen, so ist das nur möglich, indem das entsprechende Package in das Bundle importiert wird. Das wiederum ist nur möglich, wenn das zu importierende Package von seinem "Besitzerbundle" zuvor exportiert wurde. Nur exportierte Packages können importiert werden. Der Export von Packages erfolgt dabei über den Manifest Header Export-Package des exportierenden Bundles; der Import über den Header Import-Packages des importierenden Bundles.

Wie ebenfalls schon weiter oben erwähnt ist es möglich, gleich alle exportierten Packages eines Bundles zu importieren. Hierzu kann unter Dependencies ein required Plugin mit allen schon weiter erwähnten Nachteilen angegeben werden oben.

Abb.10: Definieren der zu importierenden Packages mit dem plugin.xml Editor

Abb.11: Definieren der zu exportierenden Packages mit dem plugin.xml Editor


Lebenszyklus

Ein Plugin durchläuft während seiner Existenz im Framework bestimmte Zustände. Diese geben an, in welcher Phase seines Lebens sich ein Plugin befindet. Folgendes Zustandsdiagramm stellt die möglichen Zustände dar:

Abb.3: Das Statusdiagramm für OSGi Bundles
  • INSTALLED das Bundle ist installiert
  • RESOLVED alle Abhängigkeiten des Bundles wurden aufgelöst
  • STARTING das Bundle wird gestartet
  • ACTIVE das Bundle ist gestartet
  • STOPPING das Bundle wird gestoppt
  • UNINSTALLED das Bundle ist nicht mehr installiert


Lazy Evaluation

Um eine akzeptable Startzeit der Umgebung zu erzielen, sollten möglichst nur solche Plugins bis in den Status ACTIVE gehoben werden, wenn sie auch unmittelbar benötigt werden. Alle anderen Plugins sollten zwar installiert und ihre Abhängigkeiten aufgelöst werden (Status RESOLVED), jedoch sollten sie nicht gestartet werden. Der Performancevorteil resultiert dabei aus dem Umstand, dass bis zum Staus RESOLVED lediglich die MANIFEST.MF und die plugin.xml geparsed worden sind. Ihr Inhalt wird in Datenstrukturen innerhalb des Frameworks abgelegt und Abhängigkeiten, also die Prüfung ob Packages bzw. Plugins, die von diesem Plugin benötigt werden im Framework vorhanden sind, aufgelöst werden. Alle Daten, die zur Ausführung eines Plugins notwendig, sind sind dann zu diesem Zeitpunkt im Framework gecached. Der Zugriff auf diese Daten kann dadurch in einer Art und Weise erfolgen, die die System-Ressourcen nicht übermäßig belastet. Dies gewährleistet zudem, dass Plugins im Status RESOLVED schnell aktiviert werden können. Erst wenn eine Ressource aus einem solchen Plugin benötigt wird muss das Plugin in den Zustand ACTIVE versetzt werden. Dies ist dann meist damit Verbunden, dass Klassen instantiiert werden und Speicher belegt wird, was natürlich ein besonders ressourcenintensiv ist.
Um zu erreichen, dass ein Plugin erst dann aktiviert wird, wenn auf dieses zugegriffen wird, muss der MANIFEST Header Bundle-ActivationPolicy mit dem Attribut lazy gesetzt sein.

Activator

Abb.12: Das Interface BundleActivator

Der Bundle Activator implementiert ein Interface vom Typ org.osgi.framework.BundleActivator. Es enthält die Methoden start und stop, deren Implementierungen beim Starten und Stoppen des Plugins durch das Framework aufgerufen werden. Implementiert eine der Klassen des Bundles dieses Interface und ist sie in der MANIFEST.MF als Bundle-Activator deklariert, so instantiiert das Framework diese Klasse beim Start. Hierzu muss die Klasse zwingend einen Default-Konstruktor implementieren, der dann mit Class.newInstance() aufgerufen wird. Als Parameter der start- und stop- Methode des Activators werden vom Framework der Bundle-Kontext mitgegeben. Der Activator ist somit die erste und die letzte Instanz eines Bundles, die vom Framework aufgerufen wird. Es bietet sich daher an, in ihm Ressourcen zu reservieren / besorgen und wieder freizugeben oder benötigte Instanzen zu erzeugen. Eine vereinfachte Sichtweise könnte den Activator daher auch als den Konstruktor / Destruktor eines Bundles bezeichnen.
Allerdings ist ein Activator nicht zwingend notwendig. Kommt ein Bundle auch ohne Initialisierungen aus, kann auf den Activator verzichtet werden.

Abb.13: Die Aktivatorklasse

Lässt der Entwickler sich einen Activator vom Plugin Projekt-Wizard generieren, so leitet dieser die Activatorklasse automatisch von der abstrakten Klasse AbstractUIPlugin ab. Diese Klasse wiederum implementiert das Interface BundleActivator. Die Klassse AbstractUIPlugin fügt dabei dem Activator Eclipse-spezifische Fähigkeiten wie die Verwaltung von -Preferences, -Dialogen, -Bildern und -Icons hinzu. Die Activatorklasse wird dabei vom Wizard als Singelton implementiert. Die statische Methode getDefault() bietet dabei allen Klassen des Plugins Zugriff auf den Activator selbst.

Bundle-Kontext

Abb.13: Das Interface BundleActivator

Der Bundle-Kontext ist ein Interface, welches vom OSGi Framework implementiert wird. Beim Aufruf der start Methode eines Bundle-Aktivators übergibt das Framework sich selbst in Gestalt eines Bundle-Kontextes als Parameter (der start Methode) an das Bundle. Dieses hat damit die Möglichkeit, Operationen auf dem Framework auszuführen. Nebenstehende Grafik stellt das Interface dar. Folgende Aufgaben können über den Bundle-Kontext erledigt werden:

  • Installation und Zugriff auf Bundles im Framework
  • An- und Ab- melden von Listenern
  • An- und Ab- melden eigener Services sowie Zugriff auf fremde Services
  • Erzeugen von Dateien zum persistieren von Daten, Erzeugen von Filtern


Siehe hierzu auch ein Beispiel: Registrieren von Statusänderungen von Bundles