Die OSGi Service Plattform

Aus Eclipse
Wechseln zu: Navigation, Suche

Nach der Recherche der Literatur zu meinem Thema stellte ich schnell fest, dass es zunächst notwendig ist, die Grundlagen der OSGi Service Plattform kennenzulernen. Diese ist die Grundlage des Equinox Frameworks, oder genauer: Equinox ist eine Implementierung der OSGi Plattform. Und Equinox ist das Framework, auf dem Eclipse und daher auch die Eclipse-Plugins aufgebaut sind. Allen in den OSGI Kapiteln genannten Konzepte gelten daher auch uneingeschränkt für das Equinox Framework. Ich beginne daher meine Ausführungen zunächst mit einer (knappen) Einführung in die OSGi Plattform.

Konzepte einer OSGi Plattform

Welche Konzepte werden durch eine OSGi Plattform realisiert,die es so in einer traditionellen Java Implementierung nicht gibt?
Vier Punkte sind an dieser Stelle zu nennen:

  • Modularisierung
  • Verwalten von Abhängigkeiten
  • Erweitern zur Laufzeit
  • Serviceorientiertes Programmiermodell

Modularisierung

Um sehr große Projekte beherrschbar und wartbar zu machen, ist es hilfreich, Implementierungen in kleine überschaubare Einheiten zu gliedern. Dabei sollte der Zugriff auf die eigenen Ressourcen nur über explizit zu diesem Zweck bereitgestellte Schnittstellen erfolgen.
In Java hat der Entwickler folgende Möglichkeiten den Code zu strukturieren

  • Klassen
  • Interfaces
  • Packages
  • Projekte (je nach IDE, z.B. Eclipse)

Über Zugriffsmodifier - und oft auch nur über Konventionen (z.B. Namenskonventionen) - wird signalisiert, ob ein Element von anderen Implementierungen genutzt werden darf oder nicht. Die Absicht dahinter ist, dass der Entwickler stets die Freiheit hat, seinen eigenen Code zu ändern, ohne dass er Programme zerstört, die seine Klassen nutzen. Dies setzt voraus, dass sich öffentliche Schnittstellen nie mehr ändern (höchstens erweitert werden). Klassen als das Hauptkonstrukt Code zu strukturieren sind hier nur bedingt geeignet. Dadurch, dass es möglich ist, von einer Klasse abzuleiten, können auch protected deklarierte Elemente der Superklasse genutzt werden. Fehlen in Java Zugriffsmodifier, so gelten diese Elemente als Package local, was bedeutet, dass diese Elemente im gesamten Package der Klasse sichtbar und nutzbar sind.
Besser wäre hier ein Konstrukt, das es ermöglicht, dediziert Teile des Codes zur Nutzung durch anderen Code freizugeben und andere Teile davon auszunehmen. Insbesondere sollen die Nutzung der Mechanismen der OOP (z.B. Vererbung ...) bzgl. Objekten, die nicht freigegeben wurden, unterbunden werden. Erst dann ist es möglich, dass sich der eigene Code weiterentwickeln lässt, ohne dabei fremden Code zu zerstören.
OSGi setzt genau hier an, indem es eine Einheit oberhalb von Interfaces, Klassen und Packages schafft. Es handelt sich dabei um Module, die Bundles (bei Equinox Plugins) genannt werden. Sie zeichnen sich u.a. dadurch aus, dass es möglich ist, auf Paketebene Schnittstellen für andere Entwickler sichtbar oder unsichtbar zu machen. Um ein Package nach außen hin sichtbar zu machen, muss es exportiert werden. Die Einführung von Modulen (ein Modul = ein Bundle bzw. Plugin) erweitert damit die Möglichkeiten von Java um eine weitere "kapselnde" Struktur.

Das Modul-Konzept von OSGi bietet also folgende Vorteile:

  • Quellcode wird in eigenständige Einheiten aufgeteilt
  • Schnittstellen zu diesen Einheiten können auf Packageebene definiert werden
  • nicht freigegebene Packages können nicht von fremden Code genutzt werden
  • Module können weiterentwickelt werden, ohne Code, der sich auf das eigene Modul bezieht, zu zerstören

Verwalten von Abhängigkeiten

Die Abhängigkeiten eines Moduls von einem anderem werden also auf Packageebene definiert. Voraussetzung dafür, ein Package aus einem anderem Modul zu nutzen, ist nicht nur, dass das andere Modul dieses exportiert, sondern auch, dass der Entwickler dieses explizit in sein Modul importiert hat.
Die OSGi Plattform stellt dabei einen Mechanismus zur Verfügung, der die Zuordnung von exportierten zu importierten Packages realisiert. Dieser Mechanismus stellt dabei sicher, dass innerhalb eines Moduls nur auf importierte Packages zugegriffen werden kann. Auch durch Reflection oder Manipulationen des Classloaders ist der Zugriff auf nicht importierte Packages nicht möglich.

Die in einer OSGi Anwendung installierten Module und Packages können versioniert werden. Hierdurch wird es möglich, mehrere Versionen eines Moduls gleichzeitig zu betreiben. Beim Importieren von Packages kann dann auch die Version des Moduls oder des Packages spezifiziert werden. Es ist sogar möglich, einen Versionsbereich für ein zu importierendes Modul anzugeben.
Das Importieren von Packages ist die "atomarste" Art, Zugriff auf eine Schnittstelle eines Moduls zu erlangen. Es ist aber auch möglich, ein ganzes Modul zu importieren. Das bedeutet, dass alle von diesem Modul exportierten Packages importiert werden.

Anmerkung: Package -import vs. Module -import
Abgesehen davon, dass das ein ganz bequemes Vorgehen ist, einfach ein ganzes Modul zu importieren, anstatt sich mühevoll die passende Schnittstelle aus dem Modul heraus zu suchen, hat diese Vorgehensweise jedoch auch einen großen Nachteil.
In den seltensten Fällen wird der Entwickler in seinem Modul alle Packages benötigen, die ein anderes Modul exportiert. Vielmehr werden hierdurch mehr Packages als benötigt in das eigene Modul importiert. Hierdurch wird eine unnötige Abhängigkeit zum importierten Modul geschaffen. Denn es ist durchaus denkbar, dass das benötigte Package auch von einem anderem Modul implementiert und exportiert wird. Durch das Importieren von nur dem benötigten Package in ein Bundle wird daher nur eine Abhängigkeit zu dem benötigten Package geschaffen. Durch das Importieren eines Moduls wird dagegen eine Abhängigkeit zu einem ganzem Modul geschaffen. Hierdurch ist das eigene Modul nur dann lauffähig, wenn das andere Modul ebenfalls auf der Plattform installiert ist. Anderenfalls jedoch hätte die OSGi Plattform die Möglichkeit, die Abhängigkeit durch ein anderes Modul aufzulösen.
Flexibler bleibt das eigene Modul, wenn es sich nicht von einem bestimmten anderem Modul abhängig macht.

Erweitern zur Laufzeit

Die OSGi Plattform erlaubt es, zur Laufzeit andere Module der Anwendung hinzuzufügen oder wieder zu entfernen, ohne die gesamte Anwendung zu stoppen und wieder zu starten. Dies ist ein entscheidender Vorteil für hochverfügbare Anwendungen, in denen einzelne Module hinzugefügt oder ausgetauscht werden müssen.
Der Austausch oder die Neuinstallation von Modulen erfolgt dabei über Management Agents, die in der OSGi Spezifikation beschrieben werden. Diese sind ebenfalls als Module implementiert.

Serviceorientieres Programmiermodell

Eine weitere Entkopplung der Module untereinander ermöglicht die Sicht auf ein Modul als Service. Ein Service bietet einen Dienst über eine Schnittstelle an, die nicht explizit in das dienstnutzende Modul importiert werden muss. Vielmehr kann die gewünschte Schnittstelle durch Eigenschaften beschrieben werden, die es der OSGi Plattform ermöglicht, aus den existierenden Services einen passenden auszuwählen. Im Gegensatz zu einer importieren Schnittstelle erfolgt dies aber zur Laufzeit. D.h. beide Module müssen nicht nur wie beim Import einer Schnittstelle installiert, sondern auch instantiiert sein. Welches Modul dann die gewünschte Aufgabe für das aufrufende Modul verrichtet, ist dem aufrufenden Modul zur Entwicklungszeit nicht bekannt.
Um solche Requests zu ermöglichen, stellt das OSGi eine Service Registry zur Verfügung. Diese kann im Bedarfsfall nach einer passenden Schnittstelle befragt werden. Die Abhängigkeit eines Moduls zu einem anderen wird dann erst zu diesem Zeitpunkt aufgelöst und kann von Request zu Request an ein anderes Modul gebunden werden.

Das Framework

Abb. 1 Die Schichten des OSGi Frameworks


Die Module Schicht
In ihr sind die Module oder Bundles (in Equinox die Plugins) als grundlegende Muduleinheit definiert.

Die Lebenszyklus Management Schicht
Hier werden die Zustände, die ein Modul während seiner Lebenszeit im OSGi Framework besitzen kann, spezifiziert.

Die Service Schicht
Diese Schicht spezifiziert die oben schon erwähnte Service-Registry, durch welche registrierte Module systemweit verfügbar gemacht werden.

Die Security Schicht
Hier werden sicherheitsrelevante Aspekte spezifiziert. Beispielsweise der Umgang mit signierten Modulen oder die Möglichkeit der Einschränkung der Ausführungsrechte einzelner Module.

Ausführungsumgebung

Eine der Anforderungen an die OSGi Plattform ist, dass sie auf unterschiedlichen Java Pattformen lauffähig ist. Natürlich soll das Framework auf der Java Standard Edition (JSE) lauffähig sein. Da die OSGi Plattform aber auch intensiv im Bereich der mobilen Geräte (Handys, Geräte mit abgespecktem Betriebssystemen, ...) eingesetzt wird, muss es auch auf der Java Micro Edition (JME) lauffähig sein. Insbesondere ist die OSGi Plattform unabhängig von einer bestimmten Version der JSE / JME. Um dies zu ermöglichen, definiert die OSGi Spezifikation sog. Execution Environments. Dabei handelt es sich um eine Festlegung bestimmter Klassen, Interfaces und Funktionen, die in der zugrundeliegenden Plattform vorhanden sein müssen. Zwei mögliche Execution Environments werden von der OSGi Spezifikation festgelegt:

  • OSGi/Minimum-1.1
Diese Environment legt die minimale Funktionalität, die zur Ausführung einer OSGi Plattform benötigt wird, fest. Diese Funktionalität ist auch zur Ausführung der grundlegenden Services notwendig. Es handelt sich dabei um eine Untermenge des JME Foundation Profiles. Dieses Profil ist wiederum eine Untermenge des JSE Profils, weshalb die OSGi Plattform sowohl unter JME als auch unter JSE lauffähig ist.
  • CDC-1.0/Foundation
Bei diesem Environment handelt es sich um eine Erweiterung des OSGi/Minimum -1.1 Execution Environment. Es ist ebenfalls aus dem JME Foundation Profiles abgeleitet.

Bei der Installation von OSGi kann ein Execution Environment angegeben werden. Hierdurch wird dann festgelegt, mit welcher (minimalen) Version einer JVM die OSGi Plattform lauffähig ist.

Modulschicht (Bundles, Plugins)

Die unterste logische Schicht des OSGi Frameworks ist die Modulschicht. Einheiten dieser Schicht sind die Module (= Bundles, = Plugins (in Equinox)). Ein Bundle kann dabei eigenständig im Framework installiert und deinstalliert werden. Ein Bundle ist dabei ein Jar Archiv, welches die für die gewünschte Funktionalität notwendigen Klassen und Ressourcen enthält. Die notwendigen Ressourcen können ebenfalls wiederum Jar Archive enthalten. Der Unterschied zu einem ganz gewöhnlichen Jar ist jedoch die zwingend notwendige Manifest-Datei. Diese beschreibt alle Abhängigkeiten, die ein Bundle enthält deklarativ. Das OSGi Framework benötigt die Manifest-Datei, um das Bundle installieren und betreiben zu können.

Lifecycle Management

In der Lifecycle Managementschicht werden die Zustände definiert, die ein Bundle "während seines Lebens" innerhalb der OSGi Plattform einnehmen kann.
Des weiteren werden in der Livecycle-Schicht Aktionen beschrieben, die zu einer Änderung der Zustände eines Bundles führen. Dabei werden die Zustände eines Bundles über sog. Management Agents geändert. Ein Management Agent implementiert dabei eine Schnittstelle zur OSGi Plattform, über die die Plattform von außen gesteuert werden kann. Wie die Benutzerschnittstelle zu dem Management Agent aussehen kann, wird nicht spezifiziert. Aus diesem Grunde gibt es viele Schnittstellenimplementationen, GUIs und auch Kommandozeileninterpreter.

Service Schicht

Ein OSGi Service ist ein einfaches Java Objekt. Dieses kann an der sog. Service Registry angemeldet werden. Als Anmeldenamen dient dabei der Interfacename, unter dem die Klasse ihre Dienste bereitstellt. Solche Interfaces werden daher auch Serviceinterfaces genannt. Möchte ein Modul einen solchen Service nutzen, so kann es diesen bei der Serviceregistry erfragen und über das Serviceinterface den Dienst abrufen.
Bei Services handelt es sich aber ebenfalls um dynamische Objekte, die jederzeit registriert und wieder abgemeldet werden können. Es ist also vor der Verwendung eines Services notwendig, zu prüfen, ob der Service zur Verfügung steht. Hierzu werden in der OSGi Spezifikation 3 verschiedene Möglichkeiten beschrieben:

  • Service Listener
  • Service Tracker
  • Declerative Services

Security Schicht

Die Security Schicht basiert auf dem Sicherheitsmodell, das mit dem JDK 1.2 eingeführt wurde. Dieses Sicherheitsmodell wurde, um den Anforderungen des OSGi Framework zu genügen, erweitert. So ist es möglich, Ausführungsrechte für jedes Modul individuell zu definieren.

Spezielle Framework Services

Jedes OSGi Framework enthält Standard Services, deren Funktionalität von den auf der Plattform installierten Modulen genutzt werden kann. Es folgt ein beispielhafter Auszug aus den Standard Services. Für eine vollständige Liste verweise ich auf die OSGi Spezifikation.

  • Declarative Services
  • Config Admin Service
  • Metatype Service
  • Event Admin Service
  • Log Service
  • User Admin Service
  • Http Service
  • ...