Anwendungen im laufenden Betrieb anpassen

Hot-Plugging mit Eclipse RCP und OSGi

Eugen Reiswich

Mit der Einführung des OSGi-Frameworks in die Eclipse Rich Client Platform (RCP) wurde die Möglichkeit geschaffen, Komponenten in einer RCP-Anwendung im laufenden Betrieb zu aktualisieren, neue Komponenten zu installieren oder bestehende zu deinstallieren. Damit besteht theoretisch die Möglichkeit, einen 24/7-Betrieb zu realisieren und dabei gleichzeitig die Software immer auf dem neuesten Stand zu halten. In vielen Vorträgen zu OSGi und Eclipse RCP wird gerade diese Eigenschaft besonders hervorgehoben. Doch wie immer steckt auch hier der Teufel im Detail.

In diesem Artikel werden die verschiedenen Formen der Plug-in-Interaktion in Eclipse-RCP-Anwendungen vorgestellt und die sich daraus ergebenden Herausforderungen und Fallgruben beim Hot-Plugging beschrieben. Die Möglichkeit, Komponenten einer Anwendung zur Laufzeit zu aktualisieren oder diese nachträglich hinzuzufügen oder zu entfernen, führt erfahrungsgemäß zu heißen Diskussionen über den Sinn und Unsinn dieser Eigenschaft. Die Aktualisierung einer Überweisungskomponente bei einer Sparkassenanwendung im laufenden Betrieb wird höchstwahrscheinlich problematisch verlaufen. Dagegen kann es durchaus Sinn machen, eine Reporting-Komponente zu aktualisieren, um den Informationsgehalt der Reporte anzureichern, ohne dabei die gesamte Anwendung neu starten zu müssen. Hier geht es allerdings nicht um die Frage nach dem Sinn und Unsinn von Hot-Plugging, diese Frage muss jedes Projektteam für sich selbst beantworten. Sollten Sie sich bereits für Hot-Plugging entschiedenen haben, zeigt dieser Artikel, worauf Sie besonders achten und welche Kompromisse Sie bislang noch eingehen müssen.

Plug-ins vs. Bundles

Eine Eclipse RCP-Anwendung besteht aus einer Menge von Modulen, die als „Plug-ins“ bezeichnet werden. Plug-ins können ihre Funktionalität anderen Plug-ins zur Verfügung stellen und dadurch interagieren. Dabei gibt es unterschiedliche Interaktionsmöglichkeiten, die ihre Ursprünge sowohl in der Eclipse- als auch der OSGi-Welt haben. In einer Eclipse-RCP-Anwendung verschmelzen diese Welten und vereinen ihre Fähigkeiten. So ist ein Plug-in gleichzeitig auch ein OSGi-Modul, das als „Bundle“ bezeichnet wird, mit dem Unterschied, dass es noch eine zusätzliche plugin.xml aufweist, in der Eclipse-typische Mechanismen wie Erweiterungspunkte (engl. Extension Points) und Erweiterungen (engl. Extensions) definiert werden. Die in der Literatur [1] oft gelesene Umkehrung, ein Bundle ist auch ein Plug-in, gilt jedoch nur eingeschränkt. Werden Plug-ins auf OSGi-konformen Servern ausgeführt, können die Eclipse-Mechanismen, Erweiterungspunkte und Erweiterungen nicht verwendet werden, da diese nicht Bestandteil der OSGi-Spezifikation sind. Die OSGi-Implementierung Apache Felix [2] macht hier zwar eine Ausnahme, indem sie ähnliche Mechanismen anbietet. Durch die Verwendung dieser Mechanismen würde man sich jedoch von einer speziellen OSGi-Implementierung abhängig machen.

Plug-in-Interaktionsmechanismen

Plug-ins können anderen Plug-ins Schnittstellen und Klassen zur Verfügung stellen, Erweiterungspunkte definieren und dafür Erweiterungen anbieten, sowie OSGi-Dienste bereitstellen und nutzen. Dabei sind Plug-ins lose gekoppelt und können im laufenden Betrieb ausgetauscht, hinzugefügt oder entfernt werden, ohne dabei die gesamte Anwendung zwingend neu starten zu müssen. Ermöglicht wird dies u. a. durch das OSGi-Modulkonzept, in dem jedes Plug-in als eigenständiges Modul in einem OSGi-Framework ausgeführt werden kann. In diesem Framework kann ein Plug-in verschiedene Zustände während seines Lebenszyklus einnehmen (Abb. 1). Diesen Zuständen kommt gerade beim Hot-Plugging eine wichtige Bedeutung zu.

Abb. 1: Plug-in-Lebenszyklus

Bevor ein Plug-in installiert werden kann (Abb.1: Installed), muss es physikalisch verfügbar gemacht werden. Nach der Installation werden alle Plug-in-Abhängigkeiten aufgelöst (Resolved), so dass das Plug-in gestartet werden kann. Ein Plug-in im Zustand Active ist voll funktionsfähig, kann gestoppt und bei Bedarf deinstalliert werden. Wie Abbildung 1 verdeutlicht, kann ein Plug-in entweder in dem Zustand Installed oder Resolved aktualisiert werden. Dafür müssen ggf. laufende Plug-ins gestoppt werden. Das Stoppen eines Plug-ins sowie das Starten eines neu installierten Plug-ins in einer laufenden Anwendung kann jedoch zu Problemen führen. Jede Interaktionsmöglichkeit bei Plug-ins schafft dabei eigene Probleme, die beim Hot-Plugging berücksichtigt werden müssen. In den nächsten Abschnitten werden die unterschiedlichen Interaktionsmöglichkeiten von Plug-ins einzeln und im Detail betrachtet sowie Probleme und ausgewählte Lösungen aufzeigt.

Plug-ins als Bibliothek nutzen

In OSGi sind Schnittstellen und Klassen standardmäßig außerhalb eines Plug-ins nicht sichtbar. Möchte ein Plug-in seine Schnittstellen und Klassen anderen Plug-ins zu Verfügung stellen, muss es die entsprechenden Packages explizit exportieren und damit zum öffentlichen API hinzufügen. Auf der anderen Seite müssen Plug-ins, bevor sie Schnittstellen und Klassen verwenden können, die entsprechenden Packages importieren. Durch dieses Konzept können Plug-ins von anderen Plug-ins als Bibliotheken verwendet werden.

Um neue Schnittstellen und Klassen eines Plug-ins einer laufenden Anwendung hinzuzufügen, muss eine Möglichkeit existieren, diese zur Laufzeit dynamisch nachzuladen. Dies ermöglicht OSGi durch eine eigene Klassenlader-Strategie, bei der jedes Plug-in über einen eigenen Klassenlader verfügt. Dieser ist für das Laden aller Klassen in einem Plug-in verantwortlich. Benötigt ein Plug-in Klassen anderer Plug-ins, delegiert der eigene Klassenlader diese Aufgabe an die entsprechenden Klassenlader der anderen Plug-ins. Diese Klassenlader-Strategie stellt sicher, dass alle Klassen lediglich durch einen Klassenlader geladen und Klassenlader-Probleme vermieden werden [3]. Diese Vorgehensweise ermöglicht zwar das dynamische Hinzufügen von Klassen in eine laufende Anwendung, löst jedoch nicht das Problem der dynamischen Aktualisierung von Plug-ins. Java verfügt grundsätzlich über eine sehr eingeschränkte Aktualisierungsmöglichkeit von Klassen. Dabei kann eine geladene Klasse zur Laufzeit nicht erneut geladen bzw. entladen werden [4]. Werden Plug-ins aktualisiert bzw. deinstalliert, können daher die „veralteten“ Exemplare einer Klasse weiterhin verwendet werden [5]. Dadurch können deinstallierte Plug-ins nicht vom Garbage Collector abgeräumt werden, solange andere Plug-ins ihre Funktionalität nutzen, was langfristig zu Speicherproblemen führen kann. Aktualisierte Plug-ins können zudem andere Packages exportieren, was mit der Änderung eines öffentlichen APIs vergleichbar ist.

Nach der Aktualisierung bzw. Deinstallation eines Plug-ins ist es daher notwendig, alle Benutzt-Beziehungen zu dem aktualisierten Plug-in zu erneuern bzw. freizugeben. Dafür kann der OSGi-Befehl refresh auf dem aktualisierten Plug-in aufgerufen werden. Dieser sorgt dafür, dass das OSGi-Framework zunächst analysiert, auf welche Plug-ins sich eine Aktualisierung auswirkt. Diese Plug-ins werden dann zu einer „Plug-in-Änderungsmenge“ zusammengestellt. In dieser Menge werden danach alle Plug-ins ggf. zunächst gestoppt, anschließend neu gestartet und der Graf aller Benutzt-Beziehungen neu aufgebaut [6]. Hierbei muss darauf geachtet werden, dass alle Ressourcen wie Datenbankverbindungen oder Grafiken beim Stoppen eines Plug-ins ordentlich geschlossen bzw. freigegeben werden, da diese beim Starten meist neu erzeugt werden.

Bei der soeben beschriebenen Vorgehensweise werden zunächst alle von einer Änderung betroffenen Plug-ins durch das OSGi-Framework ggf. gestoppt und anschließend neu gestartet. Fasst man den Begriff „Hot-Plugging“ genauer, so ist dies kein echtes Hot-Plugging, sondern der Neustart einer Anwendung in einem kleineren Umfang. Wie man echtes Hot-Plugging in RCP-Anwendungen ermöglicht, stellen Gregersen & Jorgensen [7] in ihrem auf dem Proxy-Pattern basierenden Ansatz vor. Sie erzeugen dabei Proxies für die veralteten Objekte und leiten alle Anfragen an die alten Objekte auf die neuen um. Diesen Ansatz integrieren die Autoren in eine modifizierte Eclipse-Laufzeitumgebung, um die Handhabung des Ansatzes zu erleichtern. Aber auch dieser Ansatz hat seine Nachteile. Er erfordert eine modifizierte und verlangsamte Eclipse-Laufzeitumgebung und kann nicht mit Änderungen am öffentlichen API umgehen.

Geschrieben von
Eugen Reiswich
Kommentare

Schreibe einen Kommentar

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