Zukunftssicher unterwegs mit OSGi und REST

Um die Versionierung von Services unterstützen zu können, erstellen wir in der Rolle des Herstellers einen speziellen ResourceProvider, der die Major-Version des Bundles, aus dem er genutzt wird, in den Ressourcen-URI mit aufnimmt. Damit werden Services in verschiedenen Major-Versionen automatisch an verschiedenen URIs publiziert (Listing 4). Damit die veränderten URIs auch transparent angesprochen werden können, muss zusätzlich ein Filter, der anhand der Version des Clients den angefragten URL nach demselben Schema modifiziert, implementiert und per OSGi Declarative Services zwischengeschaltet werden (Listing 5).

Listing 4: Versioned Resource Provider
package com.manufacturer.chargomatic.api;
import org.eclipselabs.restlet.ResourceProvider;
import org.osgi.framework.Version;
import org.osgi.service.component.ComponentContext;

public abstract class VersionedResourceProvider extends ResourceProvider {
// path field and getter omitted for brevity
protected void activate(ComponentContext context) {
    super.activate(context);
    Version bundleVersion = context.getBundleContext()
                            .getBundle().getVersion();
    String[] versionedPaths = super.getPaths();
    for (int i = 0; i 
Listing 5: Version Dispatching Filter
package com.manufacturer.chargomatic.filter;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.routing.Filter;

public class VersionDispatchingFilter extends Filter {
// constructor omitted for brevity
  @Override
  protected int beforeHandle(Request req, Response res) {
    String version = req.getClientInfo().getMainAgentProduct().getVersion();
    req.getResourceRef().setPath("/"+ version+req.getResourceRef().getPath());
    return super.beforeHandle(req, res);
  }
}

Für den Proof of Concept nutzen wir als Clientversion die Version des HTTP User Agents. Ein User Agent in der Version 3 würde also transparent auf Ressourcen aus Bundles mit der Major-Version 3 umgeleitet werden. Für die tatsächliche Implementierung ist diese Wahl sicherlich zu naiv: Die Clientversion direkt an die Version aller Services zu binden, erhöht die Kopplung signifikant und erschwert die flexible Weiterentwicklung. Stattdessen ist es sinnvoller, auf versionierte Vendor Media Types [10], [11] zurückzugreifen. Der Vorteil der Verwendung der HTTP Accept/Content-Type Header ist es, dass die Standard Content Negotiation [2] für die Versionierung genutzt werden kann. Spezielle Media Types könnten beispielsweise folgendermaßen aussehen:

Accept: application/vnd.chargeomatic-v3+xml, 
            application/vnd.chargeomatic-v3+atom;

Oder:

Accept: application/vnd.chargeomatic+xml;level=3

Für die Illustration der Funktionsweise im Rahmen des Proof of Concepts greifen wir nichtsdestoweniger auf die Nutzung der User-Agent-Version zurück, da der Mechanismus analog zur Verwendung der Accept Header, die Implementierung jedoch einfacher und durchsichtiger ist.

Nachdem wir den RessourceProvider mit Versionierungsunterstützung fertiggestellt haben, müssen wir die Klasse ChargingRequestResourceProvider anpassen, sodass sie diesen erweitert. Dann exportieren wir das Bundle com.powersupplier.chargeomatic in der Version 1.0.0, bevor wir weitere Änderungen durchführen. Danach passen wir die Ressource an, sodass sie die Informationen zur nächsten Fahrt darstellen kann. Wir ändern die Version in MANIFEST.MF auf 2.0.0 und fügen dem Komponentennamen der Ressource in der SCR Component Description ebenfalls die Versionsnummer hinzu. Schließlich exportieren wir auch für 2.0.0 ein Bundle und installieren beide Versionen in unsere Runtime:

osgi> install file:///tmp/com.powersupplier.chargeomatic_1.0.0.201108290006.jar
Bundle id is 29
osgi> install file:///tmp/com.powersupplier.chargeomatic_2.0.0.201108282348.jar
Bundle id is 30
osgi> start 29
osgi> start 30

Den User Agent spezifizieren wir bei cURL mit -A. Die Syntax des Parameters ist <Produkt>/<Produktversion> [12]. Wir rufen zwei Mal denselben URL auf, ändern aber beim zweiten Aufruf den Versionsteil des User-Agents-Werts von 1 auf 2. An der Antwort kann man erkennen, dass wir transparent auf die Services in den entsprechenden Versionen umgeleitet werden:

# Anfrage und Antwort in Version 1
$ curl localhost:8080/charge -A "charging-client/1" -X PUT -d  "{"vehicleId":"WAUZZZ8E99A000042"}" -H content-type:application/json 
{"vehicleId":"WAUZZZ8E99A000042","requestAcknowledged":true}

# Anfrage und Antwort in Version 2
$ curl localhost:8080/charge -A "charging-client/2" -X PUT  -d  "{"vehicleId":"WAUZZZ8E99A000042"}" -H content-type:application/json
{"nextPlannedTrip":{"startTime":"2011-08-26","lengthInKilometres":98},"vehicleId": "WAUZZZ8E99A000042","requestAcknowledged":true}

Eine sinnvolle Erweiterung der Beispielimplementierung wäre noch die Updatemöglichkeit über ein p2 Repository wie im vorherigen Abschnitt beschrieben.

So what?

Die vorgestellte Architektur bietet eine Reihe von Eigenschaften, die sowohl während der Entwicklung als auch zur Laufzeit die Evolution des Systems vereinfachen. Die verwendeten Konzepte und Technologien sind weitgehend praxiserprobt und standardisiert. Die Menge an Glue-Code, die implementiert werden muss, um diese Technologien zu integrieren, ist sehr überschaubar. Gerade hinsichtlich Modularisierung, Versionierbarkeit und verteilter Entwicklung ist der beschriebene Ansatz für Standardsoftwareprodukte mit kundenspezifischen Anpassungen sowie für über Plug-ins erweiterbare Basissysteme nutzbar. Darüber hinaus ist er proprietären Lösungen aufgrund der Standardisierung der verwendeten Komponenten und der vorhandenen Toolunterstützung überlegen. Die technische Offenheit für die Anbindung verschiedener Clients spielt auch in Enterprise-Systemen eine immer größere Rolle. Der Zugriff über alternative Endgeräte wie Smartphones und Tablets ist im Mainstream angekommen. Offene APIs, über die einzelne Funktionen in Dashboards und Portalen aufgerufen werden, sind schon lange keine Exoten in IT-Systemlandschaften mehr. REST und vor allem die Verwendung von Hypermedia bietet hierfür eine solide Basis: die Architektur des WWW.

Vehicle to Grid

Durch den Ausbau der Stromerzeugung aus regenerativen Energien wird das heute bestehende Stromnetz vor neue Herausforderungen gestellt, da die Stromerzeugung durch Wind-, Wasser- und Sonnenenergie starken Schwankungen ausgesetzt ist. In so genannten Smart Grids, also intelligenten Stromnetzen, spielen viele dezentrale Stromerzeuger eine signifikante Rolle. Zusätzlich zur Föderalisierung wird eine höhere Regelleistung benötigt, um Lastspitzen auszugleichen. Vehicle to Grid (V2G) bezeichnet ein Konzept zur Nutzung von Elektrofahrzeugflotten als Puffermedium für das öffentliche Stromnetz, das zu dieser Regelung beitragen kann. Ein Elektrofahrzeug kann damit an Ladestationen auf Anfrage Strom in das Netz einspeisen, anstatt nur die Fahrzeugbatterie zu laden [13]. Für eine sinnvolle Realisierung dieser Konzepte in der Breite sind neben passenden Geschäftsmodellen zwischen Fahrern und Netzbetreibern noch zusätzliche Kontroll- und Planungsfunktionen in Fahrzeug und Ladestation zu entwickeln. So muss der Netzbetreiber beispielsweise wissen, wann das Fahrzeug wieder vom Netz genommen wird, um den Mindestladezustand für eine vereinbarte Reichweite zum richtigen Zeitpunkt garantieren zu können [14].

REST und OSGi: State of the Art

Die Idee, RESTful APIs in OSGi Runtimes zu hosten, ist nicht neu. Erste Ansätze basierend auf Restlet und Eclipse Equinox Extension Points wurden von Khawaja Shams und Jeff Norris vom Jet Propulsion Laboratory der NASA auf der EclipseCon 2008 vorgestellt [15]. Allerdings wurde das darauf basierende Produkt Ensemble REST [16], das die NASA bereitstellte, wenig genutzt, da es strengen Lizenzauflagen unterlag [17]. 2010 wurde im Rahmen des Google Summer of Code ein Projekt zur Integration von Eclipse Equinox und Restlet [18] durchgeführt, das leider zu keinem verwertbaren Ergebnis führte. Die Mentoren des Projekts waren Jerome Louvel, einer der Köpfe hinter Restlet, und Bryan Hunt. Bryan hat vor einigen Wochen ein Projekt [4] zur Integration von OSGi Declarative Services und Restlet veröffentlicht, das in diesem Artikel verwendet wird. Neil Bartlett stellt alternativ dazu ein OSGi Extender Bundle [19] bereit, das es ebenfalls erlaubt, JAX-RS-Applikationen in OSGi deklarativ zu erzeugen [20]. Parallel dazu unterstützt die Referenzimplementierung Jersey [21] von JAX-RS [22] seit der Version 1.2 vom Mai 2010 OSGi [23].

Ein kommerzielles Produkt, das die Kombination aus OSGi und REST mit großem Erfolg nutzt, ist das Web-Content-Management-System CQ5, das Adobe durch die Akquisition von Day Software in sein Portfolio aufgenommen hat. Roy T. Fielding, der den Begriff REST und dessen Definition in seiner Dissertation [24] geprägt hat, war Chief Scientist bei Day Software und ist heute Principal Scientist bei Adobe Systems. CQ5 basiert auf dem Java Content Repository API und nutzt Open-Source-Frameworks aus dem Ökosystem der Apache Foundation wie Apache Felix, Jackrabbit und Sling.

Wolfgang Werner ist Senior Consultant und Softwarearchitekt beim IT-Beratungsunternehmen BridgingIT GmbH. Seine Hauptinteressen sind schlanke Architekturen, modernes Java, Eclipse RT und the stackless Stack. Neben technischen Themen fasziniert ihn die Funktionsweise effektiver und inspirierender Arbeitsumfelder für motivierte Entwickler in organisatorischer, wirtschaftlicher und menschlicher Hinsicht.
Kommentare

Schreibe einen Kommentar

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