Daten exponieren mit OData und Apache Olingo

Microsoft entwarf OData mit Blick auf den Austausch zwischen Softwaresystemen. Heute ist das Protokoll OASIS-Standard und kann bei der Bewältigung der Herausforderungen helfen, die heterogene Systeme mit sich bringen.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 23 Min.
Von
  • Michael Bolz
Inhaltsverzeichnis

Microsoft entwarf OData mit Blick auf den Austausch zwischen Softwaresystemen. Heute ist das Protokoll OASIS-Standard und kann bei der Bewältigung der Herausforderungen helfen, die heterogene Systeme mit sich bringen.

Folgendes Szenario sollte vielen Lesern bekannt vorkommen: Man hat unterschiedlichste isolierte Anwendungen, und plötzlich müssen sie in "die Service-Landschaft" des Unternehmens integriert werden. Und wie immer ist das im Projekt nicht nur schnell zu realisieren, sondern der Service muss einfach zu konsumieren sein und auf einem allgemein anerkannten Standard basieren.

An der Stelle setzt das Open Data Protocol (kurz OData) an, das einen Standard definiert, um Ressourcen sowohl anzubieten als auch zu konsumieren und so eine einheitliche Client-Server-Kommunikation zu realisieren. Hierzu sind die Ressourcen zunächst per Datenmodell (Entity Data Model (EDM), Metadata) zu definieren. Im Anschluss lassen sie sich per Query-Anfragen ($select, $top, $...) abfragen und mit CRUD-Operationen nach den REST-Prinzipien bearbeiten (Servicedocument).

Auf der OData-Webseite erklärt man die Funktion des Protokolls wie folgt:

"Das Open Data Protocol (OData) ermöglicht das Erstellen von REST-basierten Datendiensten, welches erlauben, Ressourcen, die über Uniform Resource Identifiers (URIs) identifiziert werden und in einem Datenmodell definiert sind, mittels der Verwendung von HTTP-Nachrichten, durch Web-Clienten zu veröffentlichen und zu bearbeiten."

Das Projekt Apache Olingo bietet hierzu eine Java-Bibliothek mit der Intention, sowohl einen OData-Service bereitzustellen als auch Unterstützung bei der Kommunikation mit OData-Services zu bieten.

In diesem ersten von zwei Artikeln zum Thema sind die Theorie hinter OData und wesentliche Punkte der Spezifikation Gegenstand der Betrachtung. Im zweiten wird anhand kurzer Code-Beispiele in Verbindung mit Olingo gezeigt, wie sich ein OData-Service aufsetzen und konsumieren lässt.

OData wurde als Standard von Microsoft entworfen, um einen Datenaustausch zwischen Softwaresystemen zu ermöglichen. Die adressierten Schwerpunkte liegen hier bei der Server-zu-Server-Kommunikation und der Möglichkeit, einen angebotenen Service anhand seines Servicedokuments und des Datenmodells (EDM) nahezu automatisch einzubinden.

Die OData-Versionen 1.0, 2.0 und 3.0 sind per Microsoft Open Specification Promise freigegeben. Danach wurde der Standard an OASIS übergeben und dort weiterentwickelt. Die aktuelle Version 4.0 wurde am 26. Februar 2014 als erste unter der Obhut von OASIS spezifizierte Version freigegeben.

Im OData-Standard werden die Definition des Datenmodells per EDM (Entity Data Model), der Austausch der Daten per Services im ServiceDocument und das Datenformat (REST, AtomPub, JSON) festgelegt. Weiterhin sind Funktionen in Form der System Query Options, Service Operations (oder auch Function Imports) und Batch Processing definiert, um den Austausch von Daten zu optimieren sowie CRUD-Szenarien mit an SQL angelehnten Funktionen zu erweitern.

So dienen die System Query Options dazu, die angefragten Daten näher zu spezifizieren, um zum Beispiel die zurückgelieferte Datenmenge zu reduzieren, indem nur die notwendigen Daten abgefragt werden (etwa per $filter zum Filtern oder per $top, um nur die ersten n-Einträge zu bekommen). Es lässt sich zudem auch die Menge der Anfragen (Roundtrips) vermindern, indem man bei einer Anfrage auf einen Eintrag bestimmte referenzierte Einträge bei der Antwort direkt mitliefert (beispielsweise durch ein $expand). Folgende System Query Options sind in OData definiert: $expand, $filter, $orderby, $format, $skip, $top, $skiptoken, $inlinecount und $select.

Die Service Operations (oder auch Function Imports) kann man mit Stored Procedures in SQL vergleichen. Sie dienen der Definition von Funktionen, die sich per URI referenzieren lassen und somit auch zusätzliche Einstiegspunkte sind. Das Batch Processing dient dazu, mehrere Requests in einem einzigen Batch-Aufruf zusammenzufassen und an den Server zur Abarbeitung zu übermitteln.

Durch die Notwendigkeit, ein Datenmodell (Entity Data Model (EDM)) für den angebotenen Service definieren zu müssen und es als Metadaten-Dokument bereitzustellen, kann sich ein Client alle notwendigen Informationen holen, um Daten mit dem Server auszutauschen. Das Metadata-Dokument muss per $metadata-Aufruf direkt unter der Service-URI abrufbar sein (etwa http://services.odata.org/OData/OData.svc/$metadata) und liegt im EDMX-Format vor (XML in Kombination mit der Conceptual Schema Definition Language).

Zusätzlich muss ein OData-Service noch ein sogenanntes Service Document bereitstellen (bspw. http://services.odata.org/OData/OData.svc), in dem die Einstiegspunkte zu den obersten Entitäten und spezielle Service Operations gelistet werden. Der Standard sieht für das Service Document das AtomPub- oder JSON-Format vor.

Das EDM definiert die Elemente Collection (Entity Set), Entry (Entity), Property, Complex Type, Link und Service Operation. Ein Entry entspricht einer Entität (zum Beispiel: ein Auto), das wiederum eine oder mehrere Properties (etwa Farbe, Räder und Hersteller) besitzt. Solch eine Property kann ein einfacher Typ, wie die Farbe als String, ein Complex Type, der wiederum aus Properties besteht, (zum Beispiel die Räder, die als eigene Properties ihre Größe und einen Typ besitzen) oder ein Link sein, der auf einen weiteren Entry verweist (etwa auf den Hersteller des Autos, der im EDM als eigener Entry definiert ist).

Da im Datenmodell auch die Verknüpfungen zwischen Datentypen (als Navigation) hinterlegt sind, kann sich ein Client anhand der Informationen sowohl alle Daten einer Entität als auch die aller mit ihr verknüpften Entitäten holen. Für Entitäten (Entry) lassen sich dann Collections (Entity Sets) definieren, um festzulegen, dass sie per Feed abgerufen werden können. Zudem bedeutet das, dass sie als Einstiegspunkte im Service Document auftauchen.

Die Service Operations (oder Function Imports) sind eine Erweiterung der Einstiegspunkte, um spezielle Funktionen des Service zu definieren und anzubieten. Als Beispiel könnte man eine Service Operation definieren, die zwei Parameter Farbe und Hersteller erwartet und als Ergebnis alle Autos liefert, die diesen Kriterien entsprechen.

Eine Service-Definition erledigt der Nutzer bereits implizit bei der Definition des Datenmodells (EDM), da das Service Document alle Collections (Entity Sets) und Service Operations enthält.

Durch das Umsetzen der REST-Prinzipien in OData sind alle Einstiegspunkte, die sich auf Collections beziehen, URIs, die bei einem GET einen Feed gemäß dem AtomPub-Format liefern. Jeder Eintrag im Feed entspricht wiederum einer Entität, die durch eine eindeutige URI referenziert wird. In der Praxis sieht es so aus, dass sich ein EntitySet per URI ../OData.svc/Cars abrufen lässt. Der Feed enthält dann mehrere Entitäten mit deren URI ../OData.svc/Cars('1'). Letztere lässt sich direkt aufrufen, um die Daten für genau diese Entität abzufragen. Weiterhin können Entwickler einzelne Properties einer Entity ebenso per URI abrufen, als Beispiel würde ../OData.svc/Cars('1')/color die Farbe des Autos liefern. Entsprechend ist nicht nur jede Entität, sondern auch jeder Datensatz eindeutig über eine URI referenzierbar.