Beideskönner. Wie das Gemini Blueprint Projekt Spring und OSGi miteinander sprechen lässt

Es könnte doch eigentlich alles so schön sein.

In der Theorie lernen wir von Programmierers Kindesalter an, wie moderne Architekturen aussehen, wie moderne objekt- und vor allem service-orientierte Konzepte funktionieren. Dass über Bausteingrenzen hinweg munter und verteilt kommuniziert wird, dass ein hoher Wiederverwendungswert einzelner Bausteine zum schnellen voranschreiten in der Projekt-Entwicklung führt und dass wir das Rad nur noch selten neu erfinden müssen. Modernes Programmieren gleicht mehr dem zusammensetzen von Bausteinen zur Entwicklung einer Lösung statt dem klassischen Entwickeln von Bausteinen selbst.

Als Spring noch das allgemein gängige Handwerkszeug war, um Bausteine von Software Architekturen elegant zu entkoppeln, gab es keine Sprachbarriere. Spring-Beans konnten sich wunderbar untereinander und mit Hibernate unterhalten, mit gut zusammengestellten Konfigurationsdateien waren schnell große Projekte aufgesetzt und zusammengepuzzelt.

Nun stellt seit einigen Jahren OSGi die spring’sche Vormachtstellung in Frage bzw. greift unterstützend und maßgebend in neuere Systemarchitekturen ein. Daher scheint es nur folgerichtig, eine verständliche Antwort auf die Frage „Was ist OSGi?“ zu suchen und zu untersuchen inwiefern OSGi mit Spring kombiniert werden kann.

Also, was ist das nun, OSGi und warum gilt es überhaupt darüber nachzudenken zukünftige Systeme in ein OSGi Umfeld zu integrieren, wenn Spring doch unsere Probleme bisher ganz vorzüglich lösen konnte?

OSGi kommt vom ursprünglich gar nicht aus der Landschaft der Verteilten Systeme sondern hatte als Aufgabe das Steuern und Warten von Systemen aus der Gebäudetechnik. Dazu musste es unter anderem die Forderung erfüllen, einzelne Module zur Laufzeit austauschbar und updatebar zu machen, eine Eigenschaft, die OSGi begehrt für andere Einsatzgebiete machte.
Die wachsende Popularität ist vor allem darauf zurückzuführen, dass Eclipse seit Version 3.0 auf OSGi setzt um die Rich Client Plattform flexibel und gleichzeitig komfortabel wartbar zu gestalten indem nun einzelne Module einen eigenen Lebenszyklus haben, nur noch lose untereinander gekoppelt sind und sich getrennt administrieren lassen.
Zwar bietet Java schon vom Grundkonzept verschiedene Möglichkeiten zur Modularisierung und zur Steuerung der Sichtbarkeit von Code (Klassen, Modifizierer, Packages), jedoch fehlte bisher der Überbau zur komfortablen Modularisierung von Programmteilen zur Laufzeit.
OSGi schließt diese Lücke, indem es die Möglichkeit bietet, Anwendungen in einzelne, getrennt lauffähige Bausteine (Bundles) zu zerteilen, die über Services miteinander kommunizieren.
Der Name OSGi war dabei ursprünglich ein Akronym für „Open Service Gateway Initiative“, gilt mittlerweile aber als für sich stehender Begriff


Man kann also sagen: Spring ist ein Framework zur losen Kopplung von Objekten über Dependency Injection während OSGi ein Framework zur losen Kopplung von Anwendungs-Modulen über Services darstellt.

Naheliegend ist daher, dass in vielen Einsatzgebieten ein gesunder Mix aus beiden Frameworks die ideale Lösungsbasis wäre. Beispielsweise, wenn man eine Datenbank an ein OSGi Umfeld anbinden möchte.
Denkbar wäre es, dass moderne Geldautomaten auf der OSGi Technologie aufgesetzt werden um Fernwartung zu ermöglichen, während man aber die sichere und erprobte Datenbankanbindung der heutigen Geldautomatengeneration beibehalten möchte. Diese wird in der Regel über eine Schicht aus Spring und Hibernate angesprochen werden.
Das führt allerdings zu einem neuen Problem. Solange wir uns in genau einer dieser beiden Kommunikationswelten beim Aufbau unserer Java-Anwendungen befinden, funktioniert alles gut. Was passiert aber, wenn sich zwei, die keine gemeinsame Sprache sprechen plötzlich unterhalten sollen?
Wenn wir Spring-Beans innerhalb eines OSGi-Bundles verwenden, funktioniert alles wie gewohnt. Sobald wir jedoch Spring-Beans über die Grenzen von OSGi-Bundles hinaus verwenden wollen, stoßen wir an schmerzhafte Grenzen. Die Bean, die im Kontext des einen Bundles schwimmt ist im anderen Bundle nicht sichtbar, nicht einmal bekannt. Spring und OSGi reden aneinander vorbei, da sie die Sprache des jeweils anderen nicht verstehen.

Wenn keiner der Beteiligten die Sprache des anderen lernen will oder kann, muss man einen Dritten ins Spiel bringen, einen Vermittler, der beide Sprachen beherrscht und der jeweils übersetzt. Einen freundlichen „man in the middle“, einen Sprachzwitter.

Glücklicherweise lässt uns die Apache Foundation hier nicht im Regen stehen, sondern liefert mittlerweile den (Sprach-)Zwitter in einem hohen Reifegrad quasi frei-Haus.

Gemini (englisch für Zwitter) heißt der Vermittler in seiner heutigen Version und ging aus dem ursprünglichen SpringDM Projekt hervor, das 2009 in das Gemini Blueprint Projekt gewandelt wurde. Unter anderem wirken am Projekt Entwickler von VMware Inc, der SAP AG und Paremus mit. Entsprechend steht das Projekt auf einer soliden Basis und wird real in der Praxis eingesetzt.

Ziel ist es, Spring-Beans über Bundle-Grenzen hinaus sichtbar und nutzbar machen zu können. Dies geschieht indem man die Beans als OSGi Services bereitstellt. Das ist die Aufgabe, die Gemini für uns erledigen soll.

2.png

Wie funktioniert das ganze technisch gesehen?
Um überhaupt im OSGi-Umfeld mit Spring zu agieren, müssen wir zunächst das Springframework als OSGi Bundles in unserem Projekt hinzufügen. Die meisten gängigen Frameworks und Libraries – so auch Spring – werden direkt als OSGi-Bundles angeboten und sind als fertig gepackte Artefakte im Maven Central Repository verfügbar. Eine kurze Suche im Web nach dem Namen des Frameworks in Kombination mit den Stichworten „Artifact“ und „OSGi bundle“ liefert meist als einen der ersten Treffer direkt die Koordinaten zum gesuchten Artefakt, die dann per Copy&Paste in die Projektkonfiguration – das Maven POM-File - eingetragen werden können.

3.jpg

Nun brauchen wir noch das Gemini-Projekt, unseren Vermittler, den wir über den gleichen Weg finden.

Der Rest ist ziemlich simpel. Das Projekt, in dem wir arbeiten benötigt ein META-INF Verzeichnis und darin einen „spring“ Ordner unter dem wir unsere Konfigurations-Dateien ablegen.

Dabei können wir unsere evtl. bereits vorhandenen Spring-Konfigurationsdateien einfach weiterverwenden und müssen nur zusätzlich mit „-osgi“ endende Dateien für die Definition der Gemini-OSGi Services hinterlegen.

6.jpg

Wollen wir nun Beans als Services außerhalb unseres Bundles anbieten, so müssen wir in den Gemini OSGi-Konfigurationen die entsprechenden Beans als Services deklarieren.
Dies geschieht indem wir für jede Bean ein „service“ Tag erstellen, im „ref“-Attribut die Bean per ID referenzieren und das implementierte Interface angeben (das wir im OSGi-Manifest nach außen sichtbar machen müssen).

7.jpg

Die Beans sind nun als OSGi Service verfügbar und können entsprechend von anderen OSGi Services konsumiert bzw. referenziert werden. Bei nativen OSGi Services geschieht dies meist ganz einfach per „Reference“ Annotation.

Da wir aber eventuell auch auf Seite des Konsumenten Spring-Beans nutzen, in denen wir unsere Services referenzieren wollen, müssen wir auch hier von OSGi nach Spring übersetzten. Es ist – nebenbei bemerkt - auch möglich, native OSGi Services auf diese Weise von Spring-Beans referenzieren zu lassen.
Die Grundstruktur ist auch hier die gleiche wie im Service anbietenden Bundle. Im „spring“-Ordner werden ein oder mehrere Gemini-OSGi-Files hinterlegt, in denen die Services per „reference“ Tag eingetragen werden, das die Service-ID und das implementierte Interface als Attribute trägt.

Nun können die Services über ihre ID in den Spring-Bean Konfigurationen, genau wie eine herkömmliche Spring-Bean, referenziert werden

Die Sprachbarriere ist damit überwunden, die Kommunikation der Spring-Beans über die Bundlegrenzen hinweg funktioniert nun, der Vermittler übersetzt fleißig für alle beteiligten.

Es ist doch eigentlich alles ganz schön.