W-JAX 08 - Declarative Services versus Spring Dynamic Modules
JM 02/09 - OSGi in kleinen Dosen 3
1. Java Core OSGi in kleinen Dosen – Teil 3
Immer in Bewegung –
Services à la OSGi
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 auseinanderzusetzen.
von Heiko Seeberger
registrieren von Services zu reagieren.
die Frage, wie man diese gestalten kann,
achdem in der fün eiligen Se-
Wie trägt nun das OSGi Service Model
sodass die Vorteile der Modularisierung
rie die technischen Grundlagen
zur losen Kopplung bei? Zum einen ab-
erhalten bleiben. Schließlich wäre nichts
von OSGi sowie Modularisie-
strahiert die Verwendung von Service
gewonnen, wenn die Module quasi zu-
rung und Laufzeitdynamik unter die Lu-
Interfaces von der konkreten Imple-
sammengeschweißt würden, sodass sie
pe genommen wurden, geht es diesmal
mentierung, sodass die Anbieter von
untrennbar miteinander verbunden
im Detail um das OSGi Service Model,
Services austauschbar sind. Zum ande-
sind. Es gilt also, die Architektur und
der dritten wesentlichen Eigenschaft
ren ermöglicht die Indirektion durch die
das Design so zu wählen, dass eine lose
von OSGi.
Service Registry, dass Services genutzt
Kopplung der Module erzielt wird.
Services und lose Kopplung werden können, ohne dass das nutzende
OSGi beantwortet diese Frage mit
Bundle mit deren Erzeugung zu tun hät-
dem OSGi Service Model (Abb. 1). Das
Im letzten Artikel haben wir Modulari-
te und somit unabhängig von konkreten
OSGi Framework stellt eine Service Re-
sierung als ein Mittel zur Reduktion von
Anbietern bleibt.
gistry zur Verfügung, an der Bundles
Komplexität und letztendlich zur Steige-
Services registrieren können. Dabei sind
rung von Produktivität, Flexibilität und
Beispiel und Entwicklungs-
Services Instanzen gewöhnlicher Klas-
Qualität in der So wareentwicklung ge-
umgebung
sen, also POJOs (Plain Old Java Objects).
priesen. Allerdings bestehen die Lösun-
Zur Registrierung dient das Service In-
gen für die Herausforderungen, mit de- Wir erweitern heute das Beispiel des
terface, ein „gewöhnlicher“ Java-Typ,
nen man sich in der Praxis konfrontiert „universellen Adressbuchs“ aus dem
in der Regel ein Interface. Dieses wird
sieht, in der Regel aus mehreren oder letzten Artikel um die Verwendung von
ebenso verwendet, um Services von der
gar zahlreichen Modulen. Diese müs- OSGi Services. Dazu wird das bereits
existierende Bundle com.weiglewilczek.
Service Registry zu erfragen. Um der
sen miteinander kollaborieren, sodass
example.osgi.contacts.inmemory Ser-
OSGi-Dynamik Rechnung zu tragen,
nach der Zerlegung in überschaubare
gibt es in Form von ServiceListeners die
Häppchen wieder die Zusammenfüh- vices registrieren und das neue Bundle
com.weiglewilczek.example.osgi.con-
Möglichkeit, auf Registrieren oder De-
rung ansteht. Dabei stellt sich natürlich
tacts.shell diese nutzen. Um Gerechtig-
keit walten zu lassen, wird diesmal – wie
Artikelserie: OSGi in kleinen Dosen schon beim „Hello World!“-Beispiel im
ersten Teil – Eclipse Equinox und PDE
Teil 1: Erste Schritte mit OSGi
eingesetzt, nachdem wir das letzte Mal
Teil 2: Immer in Bewegung – Bundles und Life Cycle
die Verwendung von Apache Felix und
Teil 3: Immer in Bewegung – Services à la OSGi
Quellcode Bnd gezeigt haben. Für dieses Beispiel
Teil 4: Alles XML oder was? – Services auf deklarative Weise
auf CD benötigt man eine aktuelle Version (3.4.1
Teil 5: Hier wird „Service“ groß geschrieben – Ausgewählte OSGi-
zum Zeitpunkt der Erstellung dieses Ar-
Standardservices
tikels) des Eclipse SDK mit PDE, z. B. das
14 javamagazin 2|2009 www.JAXenter.de
2. Java Core
Abb. 1: OSGi
Service Model
Abb. 2: OSGi-
Filtersyntax
Anzeige
zwei InMemoryContactRepositories mit
Package „Eclipse for RCP/Plug-in Devel-
Spieldaten und registrieren diese unter
opers“ [1]. Den kompletten Sourcecode
dem Service Interface ContactReposito-
des Beispiels nden Sie auf der Begleit-
ry (Listing 1).
CD oder als Download [2].
Optional können Service Properties
Services registrieren in Form von Schlüssel-Wert-Paaren ge-
setzt werden. Dabei müssen die Schlüssel
Wie bereits erwähnt, werden Services an
Strings und die Werte primitive Typen
der Service Registry des OSGi Frame-
oder solche aus java.* sein, um implizi-
works registriert. Wie die gesamte In-
teraktion mit dem OSGi Framework, ge- te Abhängigkeiten zwischen Bundles zu
schieht auch dies mithilfe des Interfaces vermeiden. In unserem Beispiel setzen
wir die Service Property contactReposi-
BundleContext. Die wichtigsten Metho-
tory.name, deren Wert einen Namen für
den hierfür lauten:
einen ContentRepository Service reprä-
ServiceRegistration registerService(String clazz, sentiert.
Object service,
Das OSGi Framework vergibt ana-
Dictionary properties);
log zu Bundles für jeden Service eine
ServiceRegistration registerService(String[] clazz,
eindeutige numerische ID und setzt die-
Object service,
se als Wert der Service Property service.
Dictionary properties);
id. Die Service Interfaces, unter denen
Der erste Parameter ist der voll quali - ein Service registriert wird, werden vom
zierte Name des Service Interfaces bzw. OSGi Framework als Wert der Service
Property objectClass (Tabelle 1) abge-
mehrerer Service Interfaces. Dabei ist
wichtig, dass der Service, der im zweiten legt.
Parameter übergeben wird, das Service Damit kommen wir zum Lebens-
Interface bzw. alle Service Interfaces zyklus von Services, der eng mit dem
implementiert. Andernfalls werden von Bundles verknüp ist. Nur wenn
diese Methoden eine IllegalArgument- sich ein Bundle im Zustand S ,
Exception werfen. In unserem Beispiel A oder S be ndet, kön-
erzeugen wir im Bundle com.weiglewil- nen über dessen BundleContext Servi-
czek.example.osgi.contacts.inmemory ces registriert werden. Typischerweise
javamagazin 2|2009 15
www.JAXenter.de
3. Java Core OSGi in kleinen Dosen – Teil 3
dessen Property service.ranking (Tabel-
geschieht das beim Starten, d. h. in der
Methode BundleActivator.start(). Mit- le 1) den höchsten Wert hat. Falls dies
zu keiner eindeutigen Entscheidung
hilfe der beim Registrieren zurückge-
gebenen ServiceRegistration können führt, wird der Service mit der kleins-
Listing 1
ten ID verwendet.
Services wieder deregistriert werden.
// First service
In unserem Beispiel erstellen wir
Allerdings ist das „manuelle“ Dere-
Hashtable<String, Object> properties = new Hashtable<String, Object>();
ein neues Bundle com.weiglewilczek.
gistrieren o gar nicht nötig, weil das
properties.put(ContactRepository.NAME, “In-memory“);
example.osgi.contacts.shell, das beim
OSGi Framework dies automatisch
Contact[] contacts = new Contact[] {
Starten alle registrierten ContactRe-
vornimmt, wenn ein Bundle gestoppt
new Contact(“John“, “Doe“),
pository Services aufruft, sodass wir
wird.
new Contact(“Max“, “Mustermann“) };
context.registerService(ContactRepository.class.getName(),
getServiceReferences() verwenden.
Services nutzen
new InMemoryContactRepository(contacts), properties);
Beide Methoden bedürfen der Prüfung
auf null, denn dies ist der Rückgabe-
Zur Nutzung von Services sind zwei
// Second service
wert, auch für getServiceReferences(),
Schritte erforderlich. Zunächst wird un-
properties = new Hashtable<String, Object>();
falls kein Service zur Anfrage passt.
ter Angabe eines Service Interfaces eine
properties.put(Constants.SERVICE_RANKING, 1);
Anschließend kann getService() auf-
oder mehrere ServiceReferences erfragt,
properties.put(ContactRepository.NAME, “In-memory-2“);
gerufen werden, wobei eine zuvor zu-
danach werden mithilfe dieser der oder
contacts = new Contact[] {
new Contact(“Another“, “One“) };
rückgelieferte ServiceReference als Pa-
die tatsächlichen Services abgerufen.
context.registerService(ContactRepository.class.getName(),
rameter übergeben wird. Aufgrund der
Die hierfür wichtigsten Methoden des
new InMemoryContactRepository(contacts), properties);
BundleContext lauten: Dynamik von OSGi muss unbedingt
nochmals auf null geprü werden (Lis-
ServiceReference getServiceReference(String clazz); ting 2), denn es könnte ja vorkommen,
ServiceReference[] getServiceReferences
dass im Moment zwischen der Abfrage
Listing 2 (String clazz, String filter);
der ServiceReference und des Service
Object getService(ServiceReference reference);
ServiceReference[] references =
dieser deregistriert wurde.
context.getServiceReferences(ContactRepository.class.
In unserem Beispiel geben wir zum
getName(), null);
einen den Wert der Service Property
Der Grund für die gerade aufgeführte
if (references != null) { // Check if any service registered
contactRepository.name aus, also den
Mehrdeutigkeit ist einfach zu erklären:
for (ServiceReference reference : references) {
Namen des ContactRepository. An-
Es können unter demselben Service In-
ContactRepository contactRepository = (ContactRepository)
schließend geben wir die Namen aller
terface beliebig viele Services registriert
context.getService(reference);
enthaltenen Contacts aus. Um das Bei-
werden, wohlgemerkt auch gar keiner.
if (contactRepository != null) { // Check again!
System.out.println(MessageFormat.format(
spiel auszuführen, legen wir eine OS-
Der Nutzer kann daher a priori nicht
“All contacts of {0}:“,
Gi Framework Run Con guration an,
wissen, wie viele Services registriert
reference.getProperty(ContactRepository.NAME)));
nehmen unsere Bundles sowie deren
sind, und muss mit dieser inhärenten
Contact[] contacts = contactRepository.getAllContacts();
Abhängigkeiten auf und starten zuerst
Mehrdeutigkeit umgehen. Die Metho-
for (Contact contact : contacts) {
com.weiglewilczek.example.osgi.con-
de getServiceReferences() ermöglicht
System.out.println(MessageFormat.format(“{0} {1}“,
tacts.inmemory und danach com.wei-
durch die Verwendung eines Filters, die
contact.getFirstName(), contact.getLastName()));
glewilczek.example.osgi.contacts.shell.
Ergebnismenge einzuschränken. Sie
...
Diese Startreihenfolge ist wichtig, da so-
liefert für alle passenden Services eine
ServiceReference zurück. Anders die wohl das Registrieren als auch das Kon-
Methode getServiceReference(). Hier sumieren der Services im Beispiel beim
Listing 3
Starten erfolgt. Wenn die Reihenfolge
wendet bereits das OSGi Framework ei-
private final class ContactRepositoryTracker extends ServiceTracker {
umgedreht wird, werden wir keinerlei
ne Heuristik an, die die Ergebnismenge
@Override
Ausgabe sehen. Dieses Verhalten ist
auf einen Service einschränkt, sofern
public Object addingService(ServiceReference reference) {
höchst problematisch, denn bei einem
überhaupt passende registriert sind.
ContactRepository contactRepository = (ContactRepository)
dynamischen modularen System kann
Dabei wird der Service zurückgeliefert,
super.addingService(reference);
if (contactRepository != null) { // Check again!
System.out.println(MessageFormat.format(“All contacts of {0}:“,
Service Property Bedeutung
reference.getProperty(ContactRepository.NAME)));
Contact[] contacts = contactRepository.getAllContacts();
objectClass: String[] Service Interfaces der Registrierung, vom OSGi Frame-
for (Contact contact : contacts) { work gesetzt
System.out.println(MessageFormat.format(“{0} {1}“,
service.id: Long Eindeutige Service ID, vom OSGi Framework gesetzt
contact.getFirstName(), contact.getLastName()));
service.ranking: Integer Wenn mehrere Services zu einer Anfrage über getService-
...
Reference() passen, dann wird derjenige mit dem höchs-
ten Wert zurückgeliefert, der Default entspricht 0
Tabelle 1: Wichtige Standard-Service-Properties
16 javamagazin 2|2009 www.JAXenter.de
4. OSGi in kleinen Dosen – Teil 3 Java Core
die Startreihenfolge kaum kontrolliert
werden.
Service Properties und Filter
Wir haben bereits eine Möglichkeit
kennengelernt, wie man Service Pro-
perties nutzen kann, und zwar als In-
formationsträger. Eine weitere Mög-
Abb. 3: Anwendung von Filtern in der Equinox Console
lichkeit von besonderer Bedeutung
ist die Verwendung in Filtern, um die
reference)
Ergebnismenge beim Abrufen von Ser- oder eine über Filter eingeschränkte Un-
public Object waitForService(long timeout)
viceReferences einzuschränken. Wie termenge erhalten.
bereits beschrieben, verwendet das
void addServiceListener(ServiceListener listener); Im Beispiel wird der ServiceTracker da-
OSGi Framework die Service Property
void addServiceListener(ServiceListener listener,
service.ranking dazu, beim Aufruf von zu verwendet, obiges Problem mit der
String filter);
getServiceReference() einen eindeu- Startreihenfolge zu lösen (Listing 3).
tigen Treffer zu ermitteln. Natürlich Indem nicht mehr zu einem festen Zeit-
können wir auch beliebige eigene Ser- Aufgrund von Nebenläufigkeit kann punkt aktiv ContactRepository Services
der Umgang mit diesen ServiceListeners
vice Properties de nieren und diese in abgerufen wird, sondern auf deren Re-
Filtern nutzen. recht diffizil sein. Soll beispielsweise gistrierung „gelauscht“, spielt es keine
Wie sieht nun ein Filter aus? OSGi eine jederzeit aktuelle Liste von Servi- Rolle mehr, welches Bundle zuerst ge-
verwendet dazu ein besonderes For- ces eines bestimmten Typs vorgehalten startet wird.
mat: Die „String Representation of werden, so besteht die Gefahr, Duplikate
Schlussbemerkung und
LDAP Search Filters“ [3]. Die Syntax zu erzeugen oder einzelne Services aus-
Ausblick
beruht auf der polnischen Notation, zulassen. Daher spezifiziert das OSGi
Service Compendium mit dem Service
bei der zuerst die Operatoren und da- Das OSGi Service Model komplettiert
Tracker eine Hilfsklasse, die mögliche Ra-
nach die Operanden geschrieben wer- die bereits im letzten Teil vorgestell-
den. Abbildung 2 visualisiert die Filter- ce Conditons und andere Schwierigkei- ten Eigenschaften der Modularisie-
syntax in einem Syntaxdiagramm. Im ten berücksichtigt und damit die Beherr- rung und Laufzeitdynamik. Das OSGi
Folgenden zwei Beispiele: schung der Servicedynamik vereinfacht. Framework vereinigt diese Kernprin-
Wir empfehlen, in der Regel nicht direkt zipien zu einem dynamischen und
(objectClass=com.weiglewilczek*) mit ServiceListeners zu arbeiten, sondern serviceorientieren Modulsystem, das
(&(objectClass=com.weiglewilczek*)(service.
den ServiceTracker zu verwenden. Dieser großen und vielfältigen Nutzen für die
ranking>=10))
bietet nicht nur die gerade beschriebene Java-Entwicklung bringt, z. B. durch
Möglichkeit der Serviceliste, sondern u. a. Reduktion von Komplexität, Gewinn
auch Methoden, um auf Registrieren und an Flexibilität oder Chancen für Wie-
Unser Beispiel ist zu einfach, um Fil-
Deregistrieren zu reagieren oder eine be- derverwendung. Im nächsten Teil
ter im Code zu verwenden. Aber die
stimmte Zeit auf einen Service zu warten. wenden wir uns einem fortgeschritte-
Equinox Console ermöglicht bei der
nen ema zu: Der Möglichkeit, OSGi
Verwendung des Kommandos die An-
deklarativ und komponentenorientiert
gabe eines Filterausdrucks. Wenn wir public Object[] getServices()
einzusetzen.
das Beispiel starten und den ersten public Object addingService(ServiceReference
oben aufgeführten Beispiel lter einge-
ben, werden genau die beiden Services
Heiko Seeberger ist als Technical Director für die Weigle Wilczek GmbH
ausgegeben (Abb. 3).
tätig. Sein technischer Schwerpunkt liegt in der Entwicklung von Unterneh-
mensanwendungen mit OSGi, Eclipse RCP, Spring, AspectJ und Java EE.
Services und Dynamik Seine Erfahrungen aus über zehn Jahren IT-Beratung und Softwareent-
wicklung fließen in die Eclipse Training Alliance ein. Zudem ist Heiko
OSGi ist ein dynamisches System und
Seeberger aktiver Committer in Eclipse-Projekten, Autor zahlreicher Fachartikel und
dies gilt insbesondere für Services. Kon-
Redner auf einschlägigen Konferenzen.
sumenten müssen mit dieser inhärenten
Dynamik umgehen. Dafür bietet das
Links & Literatur
OSGi Framework die Möglichkeit, auf
ServiceEvents zu reagieren, also insbe- [1] Eclipse SDK: www.eclipse.org/downloads
sondere auf das Registrieren und De- [2] WeigleWilczek-Examples: www.weiglewilczek.com/examples/
registrieren. Über den BundleContext [3] String Representation of LDAP Search Filters: www.ietf.org/rfc/rfc1960.txt
können ServiceListeners angemeldet
werden, die entweder alle ServiceEvents
javamagazin 2|2009 17
www.JAXenter.de