Zukunftsentwürfe

Applikationsdesign im Zeitalter von OSGi

Stefan Zörner

OSGi ist in aller Munde. Das „nächste Ding nach Spring“ adressiert Themen, die für die Anwendungsarchitektur relevant sind. Mittel zum Abhängigkeitsmanagement etwa, oder das dynamische Servicemodell. Dieser Artikel beleuchtet einige dieser Aspekte und gibt Empfehlungen, was beim Applikationsentwurf beachtet werden sollte, um von den Möglichkeiten von OSGi zu profitieren, ohne sich von dieser Technologie allzu sehr abhängig zu machen.

OSGi positioniert sich selbst als dynamisches Modulsystem für Java. Es bringt neue Ausdrucksmittel in den Entwurf ein, etwa den Bundlebegriff als Strukturierung oberhalb der Java-Packages. Und es führt Mittel zum Abhängigkeitsmanagement ein, inklusive der Behandlung unterschiedlicher Versionen – ein Mechanismus, der bei Java fehlt. Nicht zuletzt hält mit dem Servicekonzept von OSGi ein Hauch von SOA-Paradigmen Einzug in die VM. Services und der Lebenszyklus der Bundles (Installieren, Deinstallieren, Aktualisieren im laufenden Betrieb) bieten eine bisher ungekannte Dynamik. Futter für die Anwendungsarchitektur?

Angenommen, Sie wollen ein Softwaresystem auf Basis von OSGi bauen, um Anforderungen rund um die Erweiterbarkeit oder Dynamik Ihrer Lösung zu erfüllen. Wie strukturiert man eine solche Anwendung? Das Planen von Abhängigkeiten zählt zu den zentralen Aufgaben eines Architekten – ist OSGi hier ein Werkzeug? Wie setzen Sie Bundles und Services im Design ein?

Im Folgenden werden diese Fragen diskutiert und eine ergänzende Sichtweise zu den vielen OSGi-Darstellungen, die sich eher auf technische Details konzentrieren dargelegt. Hat der Einsatz von OSGi Einfluss auf den Entwurf? Sollte man seine Seele an diese Technologie verkaufen?

Verpachte Deine Seele!

Diese Metapher stammt aus einem Buch von Tom DeMarco und anderen über Verhaltensmuster in Projekten [Tom DeMarco et al.: Adrenalin-Junkies und Formular-Zombies, Hanser, 2007].Sie passt auch hier, denn im Idealfall sollte der grundlegende Entwurf einer Applikation unabhängig sein von einer Technologie wie OSGi, das eher Infrastruktur für Anwendungen bietet. Gleiches gilt ja auch für Dependency-Injection-Frameworks wie Spring, zu dessen besonderen Merkmalen gerade seine Nichtinvasivität zählt.

Auch wenn OSGi im Moment ein beliebter Gesprächsstoff ist, ist unklar, ob es sich hierbei wirklich langfristig um die Zukunft von Java handelt. Auf das grundlegende Design eines größeren Softwaresystems, etwa die Strukturierung in Subsysteme, die Bildung von Komponenten und das Definieren von Schnittstellen, sollte der Einsatz von OSGi daher keinen Einfluss haben. Auch konkretere Arbeitsergebnisse wie etwa einzelne Komponenten sollten möglichst keine Abhängigkeit zur OSGi-API haben. Denn dies würde das Potenzial zur Wiederverwendung reduzieren und auch die Testbarkeit außerhalb des Frameworks erschweren.

Die erste konkrete Empfehlung lautet daher, den Entwurf und auch dessen Realisierung so weit wie möglich von OSGi frei zu halten. Was heißt dieses allgemeine Prinzip konkret in der Praxis? Einen ersten Ansatzpunkt liefert die Frage, wie im Entwurf mit dem Bundle-Begriff umgegangen werden sollte.

Service und Bundle

Ein OSGi-Bundle ist ein ganz normales JAR File, jedoch mit zusätzlichen Metainformationen und einem Lebenszyklus. Ein Bundle kann also Java-Klassen und -Schnittstellen sowie Ressourcen, beispielsweise Grafiken, XML-Dateien und auch native Bibliotheken beinhalten. In den Metainformationen (dem Manifest) wird insbesondere auf Paketebene vereinbart, welche Klassen und Schnittstellen das Bundle in welcher Version bereitstellt („exportiert“). Hierbei handelt es sich in der Regel nur um eine Teilmenge der enthaltenen Elemente, nur diese sind von außen sichtbar. Weiterhin beschreibt das Manifest, von welchen Paketen (wiederum mit Versionsangaben) das Bundle abhängt. Nachdem es in einem OSGi-Framework installiert wurde, wird überprüft, ob die Abhängigkeiten durch bereits installierte Bundles aufgelöst werden können (Resolving). Nur in diesem Fall kann das Bundle aktiviert werden (eine spezielle Klasse, der Bundle-Aktivator, implementiert das Verhalten) und zum Beispiel Services bereitstellen und nutzen.

Ein Service wird in OSGi mithilfe einer Java-Schnittstelle definiert und von einer Java-Klasse implementiert. In beiden Fällen entsteht keine Anhängigkeit zur OSGi-API; es handelt sich im Plain Old Java Objects/Interfaces (POJO/POJI). Ein Bundle kann einen Service an zentraler Stelle registrieren und nach Services, die eine bestimmte Schnittstelle implementieren, suchen. Genutzt werden diese, indem Methoden auf ihnen aufgerufen werden.

OSGi bietet eine hohe Dynamik. Bundles und Services können im laufenden Betrieb kommen und gehen. Das API bietet verschiedene Listener, um darauf zu reagieren.

Bundle = Komponente?

Bundles (siehe Kasten) sind der zentraler Modularisierungsmechanismus in OSGi. Man könnte daher der Idee verfallen, Bundles als zentrales Strukturierungsmittel beim Systementwurf zu verwenden, und zum Beispiel auch Abhängigkeiten auf Basis von Bundles zu definieren. Dieser Ansatz schränkt jedoch in verschiedener Hinsicht ein. Zum einen macht man ein technologiespezifisches Konzept zur Wurzel des Designs, das man eigentlich unabhängig von OSGi halten sollte. Des Weiteren ist der Bundlebegriff zur Zerlegung nur bedingt geeignet; Bundles lassen sich nicht schachteln. Und schließlich führt das Definieren von Abhängigkeiten gegen Bundles (im Manifest: Require-Bundle) dazu, dass nur eine Komponente diese auflösen kann – das geforderte Bundle. Damit wäre aber ein Großteil der Flexibilität von OSGi verspielt und die Kopplung zwischen Modulen sehr hoch. Mehr Optionen (z.B. im Bereich Deployment oder im weiteren Verlauf beim Refactoring) halten Sie sich offen, wenn Sie die Strukturierung der Software und das Definieren von Abhängigkeiten weiterhin auf Basis von Paketen durchführen. Das empfiehlt auch die OSGi-Spezifikation (Kapitel 3.13.3, „Issues With Requiring Bundles“ [OSGi Service Platform Core Specification, Release 4, Version 4.1]). Die bei Java-Paketen übliche Namenskonvention mit Punktnotation (z.B. de.oose.cert.exam) erlaubt es, Struktur ins System zu bringen und so Subsysteme und Komponenten zu bilden.

Das Schneiden von Bundles sollte ein späterer Schritt sein und kann für unterschiedliche Deployment-Szenarien der Komponenten auch unterschiedlich ausfallen. Abhängigkeiten zwischen Subsystemen und Komponenten sollten Sie hingegen früh planen und auch entscheiden, welche Teile zur öffentlichen Schnittstelle gehören sollen und welche Implementierungsdetails darstellen (Public versus Published Interfaces [OSGi Service Platform Core Specification, Release 4, Version 4.1]). Mit den Metainformationen der Bundles können Sie diese Entscheidungen auf Paketebene explizit machen.

Geschrieben von
Stefan Zörner
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.