Nachdem ich auf der Arbeit ständig damit hantiere, gibts jetzt ne kleine Anleitung bzw. Checkliste für Declarative Services (DS) unter OSGI. Was sind DS? Ganz einfach: Normalerweise müsste man per Hand codieren, welche Services ein OSGI-Bundle anbietet und welche es verwendet. Dadurch entstehen einige Probleme, vor allem muss man sich selbst darum kümmern, was passieren soll, wenn ein Service mal nicht zur Verfügung steht oder sich im Betrieb verabschiedet. Außerdem ist es für einen Außenstehenden oft schwer einzusehen, welche Services das Bundle verwendet bzw. anbietet. Die Ressourcenbelegung ist auch ein Punkt, da die Services sich sofort bei Bundle-Start registrieren und nicht erst, wenn sie benötigt werden. Hier kommen die Declarativen Services ins Spiel. Mit ihnen kann man Services per XML-Datei konfigurieren.
Die folgende Checkliste wurde unter der Verwendung von Eclipse Equinox erstellt.
Checkliste: Was wird für Declarative Services benötigt?
- Die Implementierung der Deklarative Services erfolgt durch das Bundle org.eclipse.equinox.ds. Dieses Bundle muss immer mit geladen werden, bevor die DS verwendet werden können.
- Zusätzlich wird die Klasse ComponentContext aus dem Bundle org.osgi.service.component benötigt.
- Die eigentliche Implementierung des DS erfolgt als sog. Service Component, also einer Klasse, welche den Service (besser gesagt: das Service Interface) implementiert. Im Gegensatz zu einem Activator wird diese nicht abgeleitet oder implementiert eine Interface. Sie muss trotzdem folgende zwei Funktionen enthalten, damit die ServiceComponent funktioniert. Diese Funktionieren ähnlich wie die start und stop Methoden des Bundle-Activators.
- protected void activate(Component context){…}
- protected void deactivate(Component context){…}
- Eine Konfigurationsdatei in XML: Diese Datei wird üblicherweise in einem Verzeichnis namens OSGI-INF abgelegt, der Dateiname ist beliebig. Die XML-Datei selbst folgt dem hier zu findenden Schema. Wichtig ist hier, dass alle Inhalte der XML-Datei, also Tags, Attribute aber Paket- und Klassennamen richtig geschrieben sind, ansonsten kann die Service Component nicht aktiviert werden.
- Änderung im Manifest-File des Bundles: Füge eine neue Zeile hinzu, der Inhalt ist Service-Component: OSGI-INF/service.xml (wenn die XML-Datei service.xml heißt)
Beispiel:
Hierzu ein kleines Beispiel: Eine ServiceComponent soll erstellt werden, die einen Service mit dem Interface MyServiceInterface bereitstellt. Sie soll außerdem eine Liste von Services des Interface OtherService beinhalten. Dazu bindet sie diesen Service (der auch über eine Service Component realisiert werden muss) mit der cardinality=“1..n“, so dass mindestens ein OtherService vorhanden sein muss, um die Service Component zu aktivieren. Die einzige Funktion in MyServiceInterface gibt die Anzahl der angebundenen OtherService zurück.
Service Interface:
package org.example.ds
import ...
public interface MyServiceInterface {
int getCount();
}
Implementierungs-Klasse:
package org.example.ds.internal import ... public class MyServiceImplementation implements MyServiceInterface { private ComponentContext context; private List<OtherService> servicelist = new Vector<OtherService>(0); protected void activate(ComponentContext context){ this.context = context; } protected void deactivate(ComponentContext context){ this.context = null; } protected void bindOtherService(OtherService otherservice){ this.servicelist.add(otherservice) } protected void unbindOtherService(OtherService otherservice){this.servicelist.remove(otherservice)}public int getCount(){ return this.servicelist.size();} }
Die XML-Datei im OSGI-INF Ordner:
<?xml version="1.0" encoding="UTF-8"?>
<component name="myServiceComponent">
<implementation class="org.example.ds.MyServiceImplementation"/>
<service>
<provide interface="org.example.ds.MyServiceInterface"/>
</service>
<reference
name="otherservice"
interface="org.example.ds.OtherService"
bind="bindOtherService"
unbind="unbindOtherService"
cardinality="1..n"
policy="static"
/>
</component>
Fehlersuche:
- Das Bundle startet, aber die Service Component nicht. Dies kann viele Ursachen haben:
- Schreibfehler in der Manifest- oder XML-File. Am besten nochmal nachschauen, ob alle Paket- und Klassennamen stimmen.
- Eine geforderte Referenz ist nicht verfügbar. Untersuche alle Referenzen, ob deren Service Components vollständig gestartet wurden.
- Eintrag in der Manifest-File fehlt oder ist ungültig. Mein absoluter Lieblings-Fehler
- Funktionen wie activate/deactivate bzw. bind/unbind fehlen oder sind ungültig.
- NullPointerException oder andere krude Exceptions werden geworfen: Dies kann auftreten, wenn Klassenmember in der activate-Funtion belegt werden. Diese wird nämlich erst dann aufgerufen, wenn alle Referenzen erfüllt sind, also erst nach den bind-Funktionen. Wenn in bind schon auf das Member zugegriffen wird, ist es dann noch null und die NullPointerException wird geworfen.
- Hilfe, ich sehe nicht ob sich irgendwas tut!!!
- Der Status einer Service Component kann leider nicht einfach über den status-Befehl abgefragt werden, da Service Components ein relativ neues Konzept sind.
- Eine Service Component meldet ihre Services sofort an der Service Registry an, der Befehl services gibt also keinerlei Aufschluss über den Status ser Service Component.
- Am besten kann der Lebenszyklus über den Log-Service verfolgt werden, den Equinox mitliefert. Er kann über das Bundle org.eclipse.equinox.log angebunden werden
28. September 2008 um 11:52
Interessanter Beitrag! Scheint, als seien damit die herkömmliche Services deprecated. Schade eigentlich, daß der Status der Declarative Services nicht implizit über die osgi-console verfügbar ist. Die Unterstützung in Eclipse ist wohl auch noch etwas dürftig. Alles in allem macht das jedenfalls den Eindruck als sei OSGi noch stark im Fluss. Bleibt also spannend was da noch kommt. Hast Du noch paar Quellen/Links zu diesen Themen? Eine der besten Quellen bzgl. OSGi i.A. die ich bisher fand war der Vortrag auf der eclipsecon’06: http://www.eclipsecon.org/2006/Sub.do?id=176
28. September 2008 um 20:57
Die herkömmlichen Services haben schon noch ihre Berechtigung, vor allem wenn man ein zur Laufzeit ausgewähltes Interface als Service anbieten möchte, stößt man mit den deklarativen Services (DS) an eine Grenze, da hier nur vorher bekannte Interfaces möglich sind. Allerdings finde ich für alle anderen Fälle die DS klar besser, da man sofort sieht, welche Services ein Bundle bzw. eine ServiceComponent anbietet und referenziert.
Ein frei verfügbares Tool zur Anzeige und zum Managment der DS kenne ich leider noch nicht, evtl. können da Managment Agents wie Knopflerfish oder andere proprietäre Lösungen schon mehr. Da ich diese Funktionalität allerdings schon lange auf der Arbeit bräuchte, stehe ich schon kurz davor, mir selber was zu stricken.
Informationen über OSGI und besonders über DS sind sehr rar gesäht, als große Nachschlagewerke würde ich die OSGI-Spezifikation und das OSGI-Buch empfehlen. Besonders das Buch ist Gold wert, es widmet den DS ein eigenes, umfassendes Kapitel.
12. November 2008 um 19:47
[...] den Artikel-Aufrufen liegt das Howto zu den OSGI-Declarative Services mit 156 vorne, dicht gefolgt von dem Howto zum Debuggen von ANT-Tasks mit 107. Platz 3 belegt [...]
13. Dezember 2008 um 02:25
Ich versuche das org.eclipse.equinox.ds bundle zu finden, leider erfolgslos. Wo kann ich es zum download finden? Eclipse 3.5 M3 enthält ds support für ds im editor, aber das ds bundle ist trotzdem nicht mit eclipse ausgeliefert? Sehe ich das richtig?
13. Dezember 2008 um 12:03
Du hast recht, was DS in 3.5 M3 anbelangt. Dieser Post sagt das selbe aus. Es gab auch schon Unterschiede in DS zwischen der 3.3 und 3.4 Version. Auf der OSGi-Buch-Homepage steht eine Anleitung, wie man DS unter 3.4 nachrüstet, vielleicht geht das auch unter 3.5. Ich kann leider nicht mehr dazu sagen, weil ich nur 3.4 verwende. Es könnte auch sein, dass du das Bundle org.eclipse.equinox.cm noch benötigst, damit DS funktioniert.