Artikel

 
April 2009 | Artikel

Services à la OSGi

(Link zum Artikel: http://www.it-republik.de/jaxenter/artikel/2236)

OSGi in kleinen Dosen – Teil 3

Text: Heiko Seeberger
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share
Die OSGi Service Platform (OSGi) hat sich zu einem sehr bedeutenden Standard im Java-Umfeld entwickelt. Also wird es für den engagierten Java-Entwickler allerhöchste Zeit, sich damit näher auseinander zu setzen.
Teil 1   Teil 2   

Nachdem in der fünfteiligen Serie die technischen Grundlagen von OSGi sowie Modularisierung und Laufzeitdynamik unter die Lupe genommen wurden, geht es diesmal im Detail um das OSGi Service Model, der dritten wesentliche Eigenschaft von OSGi.

Serie: OSGi in kleinen Dosen

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

Teil 2: Bundles und Life Cycle

Teil 3: Services à la OSGi

Services und lose Kopplung
Im letzten Artikel haben wir Modularisierung als ein Mittel zur Reduktion von Komplexität und letztendlich zur Steigerung von Produktivität, Flexibilität und Qualität in der Softwareentwicklung gepriesen. Allerdings bestehen die Lösungen für die Herausforderungen, mit denen man sich in der Praxis konfrontiert sieht, in der Regel aus mehreren oder gar zahlreichen Modulen. Diese müssen miteinander kollaborieren, sodass nach der Zerlegung in überschaubare Häppchen wieder die Zusammenführung ansteht. Dabei stellt sich natürlich die Frage, wie man diese gestalten kann, sodass die Vorteile der Modularisierung erhalten bleiben. Schließlich wäre nichts gewonnen, wenn die Module quasi zusammengeschweißt würden, sodass sie untrennbar miteinander verbunden sind. Es gilt also, die Architektur und das Design so zu wählen, dass eine lose Kopplung der Module erzielt wird.

OSGi beantwortet diese Frage mit dem OSGi Service Model (Abb. 1). Das OSGi Framework stellt eine Service Registry zur Verfügung, an der Bundles Services registrieren können. Dabei sind Services Instanzen gewöhnlicher Klassen, also POJOs (Plain Old Java Objects). Zur Registrierung dient das Service Interface, ein "gewöhnlicher" Java-Typ, in der Regel ein Interface. Dieses wird ebenso verwendet, um Services von der Service Registry zu erfragen. Um der OSGi-Dynamik Rechnung zu tragen, gibt es in Form von ServiceListeners die Möglichkeit, auf Registrieren oder Deregistrieren von Services zu reagieren.

Wie trägt nun das OSGi Service Model zur losen Kopplung bei? Zum einen abstrahiert die Verwendung von Service Interfaces von der konkreten Implementierung, sodass die Anbieter von Services austauschbar sind. Zum anderen ermöglicht die Indirektion durch die Service Registry, dass Services genutzt werden können, ohne dass das nutzende Bundle mit deren Erzeugung zu tun hätte und somit unabhängig von konkreten Anbietern bleibt.

Beispiel und Entwicklungsumgebung
Wir erweitern heute das Beispiel des "universellen Adressbuchs" aus dem letzten Artikel um die Verwendung von OSGi Services. Dazu wird das bereits existierende Bundle com.weiglewilczek.example.osgi.contacts.inmemory Services registrieren und das neue Bundle com.weiglewilczek.example.osgi.contacts.shell diese nutzen. Um Gerechtigkeit walten zu lassen, wird diesmal – wie schon beim "Hello world!"-Beispiel im ersten Teil – Eclipse Equinox und PDE eingesetzt, nachdem wir das letzte Mal die Verwendung von Apache Felix und Bnd gezeigt haben. Für dieses Beispiel benötigt man eine aktuelle Version (3.4.1 zum Zeitpunkt der Erstellung dieses Artikels) des Eclipse SDK mit PDE, z. B. das Package "Eclipse for RCP/Plug-in Developers" [1]. Den kompletten Sourcecode des Beispiels finden Sie hier zum Download [2].

Services registrieren
Wie bereits erwähnt, werden Services an der Service Registry des OSGi Frameworks registriert. Wie die gesamte Interaktion mit dem OSGi Framework, geschieht auch dies mithilfe des Interfaces BundleContext. Die wichtigsten Methoden hierfür lauten:

  1. ServiceRegistration registerService(String clazz,
  2. Object service,
  3. Dictionary properties);
  4. ServiceRegistration registerService(String[] clazz,
  5. Object service,
  6. Dictionary properties);


Der erste Parameter ist der voll qualifizierte Name des Service Interfaces bzw. mehrerer Service Interfaces. Dabei ist wichtig, dass der Service, der im zweiten Parameter übergeben wird, das Service Interface bzw. alle Service Interfaces implementiert. Andernfalls werden diese Methoden eine IllegalArgumentException werfen. In unserem Beispiel erzeugen wir im Bundle com.weiglewilczek.example.osgi.contacts.inmemory zwei InMemoryContactRepositories mit Spieldaten und registrieren diese unter dem Service Interface ContactRepository (Listing 1).

Listing 1
  1. // First service
  2. Hashtable<String, Object> properties = new Hashtable<String, Object>();
  3. properties.put(ContactRepository.NAME, "In-memory");
  4. Contact[] contacts = new Contact[] {
  5. new Contact("John", "Doe"),
  6. new Contact("Max", "Mustermann") };
  7. context.registerService(ContactRepository.class.getName(),
  8. new InMemoryContactRepository(contacts), properties);
  9. // Second service
  10. properties = new Hashtable<String, Object>();
  11. properties.put(Constants.SERVICE_RANKING, 1);
  12. properties.put(ContactRepository.NAME, "In-memory-2");
  13. contacts = new Contact[] {
  14. new Contact("Another", "One") };
  15. context.registerService(ContactRepository.class.getName(),
  16. new InMemoryContactRepository(contacts), properties);


Optional können Service Properties in Form von Schlüssel-Wert-Paaren gesetzt werden. Dabei müssen die Schlüssel Strings und die Werte primitive Typen oder solche aus java.* sein, um implizite Abhängigkeiten zwischen Bundles zu vermeiden. In unserem Beispiel setzen wir die Service Property contactRepository.name, deren Wert einen Namen für einen ContentRepository Service repräsentiert.

Das OSGi Framework vergibt analog zu Bundles für jeden Service eine eindeutige numerische ID und setzt diese als Wert der Service Property service.id. Die Service Interfaces, unter denen ein Service registriert wird, werden vom OSGi Framework als Wert der Service Property objectClass (Tabelle 1) abgelegt.

Damit kommen wir zum Lebenszyklus von Services, der eng mit dem von Bundles verknüpft ist. Nur wenn sich ein Bundle im Zustand STARTING, ACTIVE oder STOPPING befindet, können über dessen BundleContext Services registriert werden. Typischerweise geschieht das beim Starten, d. h. in der Methode BundleActivator.start(). Mithilfe der beim Registrieren zurückgegebenen ServiceRegistration können Services wieder deregistriert werden. Allerdings ist das "manuelle" Deregistrieren oft gar nicht nötig, weil das OSGi Framework dies automatisch vornimmt, wenn ein Bundle gestoppt wird.

Teil 1   Teil 2   

andere Artikel dieser Serie


Anzeige

Kommentare


Anzeige

zurück zum Seitenanfang