Mittwoch, 23. Mai 2012


Artikel

Februar 2009 | Artikel

OSGi in kleinen Dosen – Bundles und Life Cycle

(Link zum Artikel: http://www.entwickler.de/jaxenter//002118)

Immer in Bewegung - Zusammenspiel von Modularisierung und Laufzeitdynamik

Text: Heiko Seeberger
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share
Wir haben uns im Teil 1 der Serie "OSGi in kleinen Dosen" mit den drei herausragenden Eigenschaften von OSGi beschäftigt: Modularisierung, Laufzeitdynamik und Serviceorientierung. Jetzt ist es an der Zeit, Modularisierung und Laufzeitdynamik und deren Zusammenspiel unter die Lupe zu nehmen.
Teil 1   Teil 2   Teil 3   

Wozu brauchen wir eigentlich Modularisierung bzw. ein Module System? Die Antwort ist einfach: zur Reduktion von Komplexität. Module abstrahieren vom schwer überschaubaren Problem hin zu übersichtlichen Herausforderungen. Diese können isoliert voneinander und möglicherweise auch arbeitsteilig in Angriff genommen werden und bieten die Möglichkeit der Wiederverwendung.

Serie: OSGi in kleinen Dosen

Teil 1: Erste Schritte mit OSGi (1), (2)

Teil 2: Bundles und Life Cycle

Was bedeutet das konkret für Java? Als objektorientierte Programmiersprache bietet Java ein Modulkonzept in Form von Klassen und Packages. Allerdings ist diese Form der Modularisierung zu feingranular, um damit nicht-triviale Softwaresysteme adäquat zu modularisieren. Mit anderen Worten: Java fehlt ein Modulkonzept "oberhalb" der Packages.

Bevor wir betrachten, wie OSGi diese Lücke schließt, definieren wir zunächst, was wir eigentlich genau unter einem Modul verstehen wollen:

  • Self-contained: Ein Modul ist eine Komposition aus kleineren Teilen, mit Ausnahme von wohl definierten Abhängigkeiten und kann nur als Ganzes verwendet werden.
  • Cohesive: Die Elemente eines Moduls haben einen starken logischen Bezug zueinander.
  • Loose coupling: Module besitzen untereinander eine geringe Kopplung.
  • Public API: Ein Modul besitzt eine wohl definierte öffentliche Schnittstelle und verbirgt die Interna der Implementierung.
  • Dependencies: Die Abhängigkeiten eines Moduls sind wohl definiert.
  • Deployment format: Ein Modul „läuft“ in einem Container bzw. einer Laufzeitumgebung und wird dort in einem wohl definierten Format installiert.

Diese Eigenschaften führen nicht nur dazu, dass komplexe Softwaresysteme in überschaubare Module heruntergebrochen werden können, wodurch zweifellos die Produktivität und Qualität in der Entwicklung gesteigert werden kann. Sie fördern auch die Flexibilität und wirken sich dadurch positiv auf Time-to-Market und die Nutzung neuer Marktchancen aus.

Beispiel und Entwicklungsumgebung
Wir beginnen mit einem Beispiel, das wir in den folgenden Teilen dieser Artikelserie sukzessive ausbauen werden. Dabei handelt es sich um ein "universelles Adressbuch", das Kontakte aus beliebigen Repositories gemeinsam verwalten kann. Sämtlicher Sourcecode sowie die speziell benötigte Software können hier heruntergeladen werden.

Eclipse PDE wurde als Entwicklungswerkzeug für Bundles sowie Eclipse Equinox als OSGi-Implementierung bereits vorgestellt. Wir werden weiterhin Eclipse als Java-Entwicklungswerkzeug verwenden, aber diesmal mit Bnd und Apache Felix, und Alternativen für die Entwicklung bzw. die OSGi-Implementierung aufzeigen.

Bnd wird von Peter Kriens, Director of Technology der OSGi Alliance, entwickelt und dient unter anderem zum Erstellen von OSGi Bundles. Mithilfe von bnd-Dateien, deren Syntax der des Bundle Manifests ähnelt, sowie der Analyse des Imports von Klassendateien erstellt Bnd aus Java-Projekten OSGi Bundles. Es kann per Kommandozeile als Ant-Task oder Eclipse-Plug-in verwendet werden. Wir werden es als Eclipse Plug-in einsetzen, indem wir die aktuelle produktive Version (zum Zeitpunkt der Erstellung dieses Artikels 0.0.249, als bnd-0.0.249.jar in den Sourcen zum Artikel) in das Verzeichnis dropins unserer Eclipse-Installation kopieren und Eclipse anschließend neu starten. Danach stellt das Bnd-Plug-in im Kontextmenü von bnd-Dateien im Package Explorer den Menüpunkt MAKE BUNDLE zur Verfügung.

Um Apache Felix ebenfalls in Eclipse zu integrieren, erstellen wir ein Java-Projekt org.apache.felix und stellen in den Project Properties (Kontextmenü PROPERTIES) unter JAVA BUILD PATH | SOURCE den Default output folder auf org.apache.felix oder jeden beliebigen anderen Pfad außer org.apache.felix/bin. Anschließend entpacken wir den Download der aktuellen produktiven Version von Apache Felix (zum Zeitpunkt der Erstellung dieses Artikels 1.2.1) in das Projektverzeichnis und fügen die Datei bin/felix.jar unter JAVA BUILD PATH | LIBRARIES zu den Libraries hinzu. Dann exportieren wir diese Library unter JAVA BUILD PATH | ORDER AND EXPORT, sodass unsere Projekte auf die darin enthaltene OSGi-API Zugriff haben. Anschließend erzeugen wir mittels RUN | RUN CONFIGURATIONS … eine neue Run Configuration vom Typ Java Application, benennen Sie mit org.apache.felix, wählen org.apache.felix.Main als Main Class und setzen die folgenden VM Arguments: -Dfelix.cache.dir=.cache -Dfelix.cache.profile=default. Die Ausführung dieser Run Configuration startet Apache Felix, wobei per Default "Welcome to Felix." auf der Konsole ausgegeben wird. Die den Artikel begleitende ZIP-Datei enthält unter anderem bereits dieses Projekt, sodass wir dieses nur in unseren Workspace importieren müssen.

Modularisierung à la OSGi
Die OSGi-Spezifikation adressiert das Thema Modularisierung im Module Layer des OSGi Frameworks, der für dessen weitere Schichten die Grundlage bildet. Darin wird das Bundle als OSGi-Einheit für Modularisierung definiert. Ein Bundle besteht aus Java-Klassen und Ressourcen wie Properties-Dateien und wird als JAR-Archiv im OSGi Framework installiert (Abb. 1).

Darüber hinaus enthält ein Bundle Metadaten, z.B. über die öffentliche Schnittstelle oder die Abhängigkeiten, die vom OSGi Framework verwendet werden, um obige Eigenschaften sicherzustellen. Diese Metadaten werden im Bundle Manifest (META-INF/MANIFEST.MF) spezifiziert, das ohnehin Bestandteil der JAR-Spezifikation ist. Damit ist ein Bundle außerhalb des OSGi Frameworks ein ganz gewöhnliches JAR-Archiv und kann in jedem beliebigen Java-System verwendet werden. So sind z.B. sämtliche Libraries des Spring Frameworks seit Version 2.5 OSGi Bundles, was jedoch bei "klassischer" Verwendung völlig transparent bleibt.

Für den Einsatz innerhalb des OSGi Frameworks sind die Metadaten aus dem Bundle Manifest von entscheidender Bedeutung. Die Syntax entspricht dem standardisierten Manifest-Format. Dabei handelt es sich vereinfacht ausgedrückt um Name-Wert-Paare, ähnlich der vielfach verwendeten Properties. Ein wesentlicher Unterschied zu Properties-Dateien ist der obligatorische Doppelpunkt als Trennzeichen sowie die sehr strenge Syntax, z.B. die Begrenzung auf 72 Bytes pro Zeile, das Verbot von Leerzeichen vor dem Doppelpunkt etc. Daher ist es sehr empfehlenswert, das Bundle Manifest nicht manuell zu erstellen, sondern auf die Hilfe eines Werkzeugs wie Eclipse PDE oder Bnd zurückzugreifen. OSGi spezifiziert eine Reihe von Manifest Headern und zugehörige Syntaxvorgaben für deren Werte. Tabelle 1 gibt einen Überblick über besonders wichtige Manifest Header.

Jedes Bundle besitzt einen obligatorischen symbolischen Namen, der mittels Bundle-SymbolicName spezifiziert wird. Wir empfehlen als Best Practice, bei der Benennung die Reverse-Domain-Name-Konvention zu verwenden, um Namenskollisionen zu vermeiden. Dabei beginnt der symbolische Name mit dem umgekehrten Domain-Namen und spezifiziert dann "Sinn und Zweck", z.B. com.weiglewilczek.example.osgi.contacts.core. Gemeinsam mit der Version, die mittels Bundle-Version gesetzt wird, identifiziert der symbolische Name ein Bundle eindeutig innerhalb des OSGi Frameworks. Daraus wird unmittelbar ersichtlich, dass es möglich ist, das gleiche Bundle (mit demselben symbolischen Namen) in mehreren unterschiedlichen Versionen zu installieren. Zusammen mit der weiter unten erläuterten Möglichkeit, bei Abhängigkeiten auf bestimmte Versionen abzuzielen, bietet OSGi damit einen Ausweg aus der leider allzu häufig auftretenden Java-Sackgasse, bestimmte Libraries gleichzeitig in verschiedenen Versionen zu benötigen.

Der lokale Klassenpfad eines Bundles kann aus mehreren Verzeichnissen innerhalb des Bundles bestehen und sogar eingebettete JAR-Archive enthalten. Wichtig: Es ist nicht möglich, Klassen oder JAR-Archive außerhalb des Bundles zu verwenden. Das OSGi Framework sorgt durch eine ausgeklügelte Classloading-Architektur dafür, dass Bundles self-contained sind und ihr lokaler Klassenpfad ausschließlich aus Elementen innerhalb des Bundles besteht. Wird der entsprechende Manifest Header Bundle-Classpath weggelassen, wird per Default das Wurzelverzeichnis des Bundles verwendet. Dies ist empfehlenswert, um Bundles als "ganz normale" JAR-Libraries verwenden zu können.

Teil 1   Teil 2   Teil 3   

andere Artikel dieser Serie

Kommentare