OSGi erwacht zum Leben

Abhängen via Gemini Blueprint

Den programmatischen Zugriff auf Services haben wir bereits dargestellt. Seit der Version 4.2 definiert OSGi den so genannten Blueprint Container, der es ermöglicht, angebotene und verwendete Services deklarativ anzugeben. Abhängige Services bzw. Containerelemente werden per Dependency Injection miteinander verwoben.

Der Gemini Blueprint Container [4] ist die Referenzimplementierung. Sie ist im Rahmen des Spring-Projekts entstanden und an die Eclipse Foundation übergeben worden. Gemini unterstützt sowohl Konfigurationsdateien im Blueprint-Format als auch Spring ApplicationContext-Dateien. In Letzteren können mittels des Schemas spring-osgi.xsd Beans entweder als OSGi Services exportiert oder OSGi Services aus der Registry referenziert werden. Sie können dann wie normale Beans genutzt werden. In unserer Beispielanwendung verwendet das Provider-Bundle com.zuehlke.contacts.service.remote den Blueprint Container zum Registrieren der Implementierungen, wie Listing 3 entnommen werden kann.

Listing 3

Der programmatische Zugriff im Bundle com.zuehlke.contacts.ui funktioniert weiterhin. Denn beide Ansätze nutzen den OSGi Service Bus und sind somit interoperabel. Ein anderes Bundle könnte im ApplicationContext wie folgt auf die Services zugreifen (Listing 4).

Listing 4
.

.
Erweitern via Eclipse Extension Registry

Neben der Service Registry bietet Eclipse RCP den Extension-Point-Mechanismus an. Er kann gut genutzt werden, um erweiterbare Komponenten und die zugehörigen Erweiterungen umzusetzen. Das Arbeiten mit Extension Points und Extensions ist relativ einfach. Der Mechanismus basiert im Wesentlichen auf XML. Ein Bundle definiert einen Extension Point in einem XSD-Format und implementiert das Auslesen von Extensions, also passenden XML-Elementen. Um auffindbar zu sein, müssen diese in den jeweiligen plugin.xml der abhängigen Bundles deklariert werden. Häufig wird in der Extension eine Callback-Klasse mit angegeben. Diese Klasse implementiert ein Callback-Interface aus dem erweiterten Bundle und wird von diesem später aufgerufen.

Ein wesentlicher Punkt dabei ist, dass das anbietende Bundle die Erweiterungen verwendet, ohne explizite Abhängigkeiten zu ihnen haben zu müssen. Die Richtung der Abhängigkeit zeigt vom erweiternden auf das erweiterte Bundle, nicht umgekehrt. Dieses Konstrukt bietet so eine gute Möglichkeit, den Abhängigkeitstyp der erweiterbaren Komponente zu implementieren. Abhängigkeits- und Aufrufrichtung sind auf der rechten (blauen) Seite von Abbildung 2 veranschaulicht.

In der Beispielanwendung werden Extension Points für die Erweiterung des UI-Bundles genutzt. Der Extension Point com.zuehlke.contacts.ui.intent stellt den Anknüpfungspunkt dar, um Intents für E-Mail-Adressen und Telefonnummern zu registrieren. Die Erweiterung com.zuehlke.contacts.ui.intent.skype registriert dann einen konkreten Intent, um eine Telefonnummer per Skype anzurufen (Abb. 3). Die angegebene Klasse SkypeCallIntent implementiert dabei das Interface com.zuehlke.contacts.ui.intent.IContactIntent aus dem UI-Bundle. Die Extension-Definition ist in Listing 4 dargestellt.

Listing 4
.

.

Die Verwendung von Extension Points hat noch einen entscheidenden Vorteil. Die erweiternden Bundles können lazy, also bei Bedarf, gestartet werden. Die Extensions können ausgelesen und in der Oberfläche dargestellt werden, ohne dass die Callback-Klasse oder das umliegende Bundle geladen sein müssen. Das Bundle muss lediglich im Status RESOLVED sein. Das Laden der Callback-Klasse und damit auch das Starten des Bundles geschehen beim ersten Zugriff auf die Erweiterung, im Beispiel also, wenn der Intent vom Benutzer ausgewählt wird. Der Aufruf wird dann an die geladene Klasse delegiert.

Abb. 3: Kontakteditor mit Erweiterungen
Kommentare

Schreibe einen Kommentar

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