Teil 1: Schnittstellen und Abhängigkeiten in OSGi-basierten Systemen

Abhängen à la OSGi

Stefan Reichert und Pascal Alich

Die Softwareplattform OSGi ist mit ihren Bundles und Services per se komponentenorientiert. Für darauf basierende Anwendungen stellt sich daher die Frage, wie die Komponenten geschnitten und deren Schnittstellen definiert werden sollen. Dieser Artikel zeigt bewährte Lösungen für typische Anforderungen auf. Geringe Komplexität und hohe Flexibilität werden dabei gegeneinander abgewogen. Im ersten Teil der Serie gehen wir auf die Komponentenorientierung und unterschiedliche Komponententypen ein.

OSGi ermöglicht es, Java-Anwendungen zu modularisieren. Konventionelle Java-Anwendungen nutzen lediglich Packages zur Unterteilung und JAR-Dateien zur Verteilung der Software. Abhängigkeiten zwischen den Komponenten werden üblicherweise zur Designzeit definiert und müssen zur Entwicklungszeit durch separate Tools sichergestellt werden, denn prinzipiell kann aus allen Teilen der Software auf alle anderen zugegriffen werden. OSGi unterstützt die Modularisierung hingegen dadurch, dass Komponenten und Abhängigkeiten sowohl zur Entwicklungs- als auch zur Laufzeit aktiv verwaltet werden. Zugriffe zwischen den Komponenten sind nur in dem definierten Maße möglich.

Komponente = Bundle

Komponenten in OSGi heißen Bundles und beinhalten Klassen und Dateien, ergänzt um Metainformationen, wie Name und Version, sowie die Deklaration von Abhängigkeiten und der eigenen Schnittstelle. Letzteres wird durch den Export von Packages erreicht. Auf die nicht exportierten, also internen Packages, kann kein anderes Bundle zugreifen. Ergänzt wird dieses Komponentenmodell von der Service Registry, bei der Serviceimplementierungen registriert und von anderen Bundles bezogen werden können. Dies ist insofern sinnvoll, als dass die Implementierung leichter austauschbar ist und der Nutzer auch nicht wissen muss, wie der Service instanziiert wird. Diese Einleitung dient sicher nicht als vollwertige OSGi-Einführung, sondern soll lediglich die für diesen Artikel relevanten Begriffe in Erinnerung rufen.

Komplexität runter, Flexibilität hoch

Komponentenorientierung ist ein etabliertes Paradigma für die Architektur von Softwaresystemen. Dabei wird das System in logisch zusammengehörende Teile gegliedert. Das kann sowohl nach technischen als auch nach fachlichen Gesichtspunkten geschehen. Dabei ist ein Grundgedanke, dass die einzelnen Komponenten des Systems leichter zu beherrschen sind als ein großes System. Allerdings wirkt sich die Anzahl der Komponenten natürlich auf die Komplexität des Gesamtsystems aus. Ein System ist umso komplexer, je mehr Komponenten und je mehr Abhängigkeiten es zwischen den Komponenten es gibt. Es ist daher auch für ein auf OSGi-basiertes Softwaresystem klar, dass eine möglichst geringe Komplexität erstrebenswert ist. Denn mit zunehmender Komplexität steigen die Kosten für Wartung und Weiterentwicklung. Dem entgegen stehen Anforderungen nach Flexibilität, wie zum Beispiel Austauschbarkeit von Komponenten oder Erweiterbarkeit des Systems.

Beim Design der Komponenten ist daher weder maximale Flexibilität noch minimale Komplexität das Ziel. Vielmehr sollte das Gesamtsystem so flexibel wie nötig, aber dabei so wenig komplex wie möglich sein. Gleiches gilt für die Komponenten selbst. Auch für sie muss jeweils aus den Anforderungen die erforderliche Flexibilität bestimmt werden. Im weiteren Verlauf des Artikels werden gängige Anforderungen und zugehörige Lösungsmuster für den Entwurf einer Komponente beschrieben, die unserer Meinung nach den genannten Anforderungen genügen.

Abhängigkeitstyp „Komponente“

Bei dieser grundlegendsten Art von Abhängigkeit wird eine anbietende Komponente, der Provider, von einer anderen Komponente, dem Consumer, verwendet. Schnittstelle und Implementierung liegen dabei zusammen im Provider (Abb. 1). Im Fokus stehen zum einen die Kapselung und zum anderen die Wiederverwendbarkeit der Implementierung. Die Consumer binden sich lediglich an die Schnittstelle einer Komponente, nicht aber an die Implementierung. Letztere kann demnach flexibel angepasst werden, ohne die abhängigen Komponenten zu beeinflussen. Die Kontrolle über die Schnittstelle liegt in diesem Szenario bei dem Provider.

Abb. 1: Abhängigkeitstyp „Komponente“

Bei der Frage, wie eine Funktionalität auf Komponenten aufgeteilt wird, spielt es im Übrigen keine Rolle, ob es sich um technische oder fachliche Aspekte handelt. Vielmehr sollte die Frage nach der erforderlichen Flexibilität ausschlaggebend sein. Im Sinne der Komplexitätsreduzierung sollte zu Beginn immer dieser einfachste Typ erwogen werden. Stellen Sie sich dazu eine Security-Funktionalität vor, die zunächst als eine Komponente entwickelt wird: Die Schnittstelle enthält die Java-Interfaces auf die Funktionen Login und Rechteprüfung sowie die benötigten Fachklassen, wie Benutzer, Rolle und Recht. Weiterhin ist auch die Implementierung der Interfaces enthalten, inklusive der Datenbeschaffung zum Beispiel über LDAP und die Geschäftslogik. Vorerst besteht keine Notwendigkeit, von diesem Ansatz einer einzelnen Komponente abzuweichen.

Geschrieben von
Stefan Reichert und Pascal Alich
Kommentare

Schreibe einen Kommentar

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