Die Problemstellung: Einlesen von Daten aus einer OData-Quelle in Talend

Bei der Zusammenführung unterschiedlicher Datenquellen zur weiteren Verarbeitung mittels Talend stößt man auch auf Daten, die man mithilfe eines Webservice abrufen kann. Hierzu gehören z.B. unter anderem OData-Schnittstellen. Bei OData handelt es sich um einen Standard für REST-Webschnittstellen.

Zur Kommunikation mit REST-Webservices existiert in Talend z.B. die tREST Komponente. Um hier aber die Authentifizierung korrekt durchzuführen (stellen Sie sich z.B. vor, ihr Internetverkehr muss über einen Proxy laufen, der auf die Authentifizierung via NTLM Wert legt), die gewünschten Daten aus der XML-Antwort auszulesen oder Daten nur blockweise vom Webservice abzufragen, benötigt man mit tREST einige weitere Schritte bei der Erstellung eines Jobs, vor allem bei der Anbindung mehrerer, unterschiedlicher OData-Quellen.

Der Ansatz: Auslese von OData-Quellen mit Java

Eine weitere mögliche Lösung zur Anbindung von OData-Quellen stellt die Verwendung der Apache Olingo Bibliothek dar. Diese bringt mehrere komfortable Klassen und Methoden mit. Sie bietet zum Beispiel die Möglichkeit, Objekteigenschaften in primitive und nicht-primitive Werte einzuteilen und Anfragen mittels top, skip oder select Kommandos zu strukturieren. Es kann auch eine passender Java-Datentyp zu einer Eigenschaft aus den Metadaten des OData-Services mithilfe des Tools bestimmt werden.

Da Talend mit seinen Routinen bereits eine einfache Möglichkeit bietet, eigenen Java Code in Projekte einzubinden, kann die Olingo Bibliothek leicht in einen Talendjob integriert werden. Dabei bietet sich die Verwendung einer tJavaFlex Komponente an. Der Code in dieser Komponente und der Routine kann in weiten Teilen für unterschiedliche OData-Quellen wiederverwendet werden. Die einzigen Informationen, die ausgetauscht werden müssen, sind das Schema und der Name der Output-Row der tJavaFlex Komponente.

Die Vorteile der Auslese mit Java

Durch eine derartige Datenabfrage eröffnen sich mehrere interessante Möglichkeiten.

Durch die Verwendung von Schleifen beim Ansprechen des Webservice kann der Nutzer diese auch nur „stückweise“ (z.B. immer nur 1000 Datensätze auf einmal) auslesen. Große Datenmengen belasten so nicht den Webservice, da einzelne Verbindungen nicht aufrecht gehalten werden. Weiterhin können die ersten Datensätze nach ihrer Auslese bereits in Talend weiterverarbeitet werden. Durch die Implementierung der Auslese in einer Routine und der Nutzung dieser in einer tJavaFlex Komponente wird ein aufwendiges Triggern der einzelnen Ausleseschritte im Talendjob vermieden.

Da der ODataClient der Olingo-Bibliothek seine Verbindungen basierend auf Klassen aus dem org.apache.http Packages aufbaut, kann man die Möglichkeiten dieses Packages nutzen, um auf Besonderheiten im Verbindungsaufbau einzugehen. So ist es z.B. möglich, die Basic Authentication direkt als Base64-String im Header der Anfrage zu hinterlegen oder die Verbindung über einen Proxy mit NTLM Authentifizierung aufzubauen und die Datenauslese so auf die Anforderungen der eigenen Infrastruktur abzustimmen. Hierdurch erreicht man im Vergleich zur Verwendung von Talend-Komponenten eine erhöhte Flexibilität.

Mittels der Olingo-Bibliothek ist man in der Lage, aus dem Metadaten-Dokument eines OData-Services die passenden Java-Datentypen für eine Objekteigenschaft auszulesen. Damit ist es möglich, ausgelesene Werte direkt automatisiert auf passende Datentypen zu casten und gegebenenfalls auch entsprechend vorzubereiten. So kann man im Code der Routine beispielsweise Datenwerte erkennen und einen ausgelesenen String korrekt in ein java.util.Date umwandeln.

Im Code der Routine kann man durch Verwendung des Interfaces routines.system.IPersistableRow und der Nutzung von Reflection das Schreiben von Daten in eine Output-Row realisieren. Mit einer passenden Namensgebung für die Spalten im Schema der tJavaFlex Komponente (analog zur OData-Datenquelle) kann man die ausgelesen Werte so automatisiert in die korrekten Felder der Output-Row schreiben.

Dadurch, dass man den Javacode Quell- und Zielsystem-unabhängig schreibt, kann man ihn in unterschiedlichen Talendjobs wiederverwenden und spart so Aufwand für die Implementierung. Lediglich die tJavaFlex Komponente muss geringfügig an das neue Quellsystem angepasst werden.

Die Auslese im Beispiel

Im Folgenden soll die Verwendung einer selbstgeschrieben OData-Routine im Talend Studio gezeigt werden. Hierzu wird beispielhaft der öffentlich verfügbare OData-Service des U.S. Department of Transportation verwendet. Hier wollen wir das „Railroad Operations Data“-Datenset auslesen.

1. Durch Prüfung des Servicedokuments und der Metadaten sehen wir, dass mit „q3zc-9e5e“ sowohl eine Entity (diese kann man sich als „Eintragstyp“ vorstellen; sie zeigt uns unser Schema) als auch eine Collection, die „Railroad Operations Data“ benannt ist.

2. Im Talend Studio legen wir nun ein generisches Schema an, welches zur gerade untersuchten Entity passt. Hierbei müssen wir die passenden Java-Datentypen wählen (ein Int64 wird zu einem Long, ein Decimal zu einem BigDecimal, etc.).

3. Anschließen können wir beginnen, unseren Job zu erstellen. Zur Verbindung mit dem OData-Service benötigen wir einige Variablen, die wir hier im Context des Talendjobs anlegen.

Den namespace finden wir im Metadaten-Dokument, die anderen Variablen wurden bereits erklärt. Nutzer und Passwort müssen nicht gesetzt werden, da der Service keine Zugangsdaten verlangt.

4. Mit der von uns erstellten Routine ODataReadout ist es nun relativ einfach möglich, eine grundlegende Auslese der OData-Quelle zu realisieren.

Hierzu benötigen wir nur eine tJavaFlex Komponente. Dieser weisen wir das gerade erstellte Schema zu. Im Start code Block lesen wir zuerst das Schema der Einträge als Map aus. Diese enthält nun den „Spaltennamen“ und den zugehörigen Datentyp bzw. die zugehörige Klasse. Anschließend lesen wir die Entities aus, die der OData-Service zurückgibt. Für jede Entity schreiben wir dann im Main code Block die Eigenschaften (die „Werte“ bzw. die Einträge in den entsprechenden Spalten) einer Entity mit zum Schema passenden Datentypen (die unserem angelegten, generischen Schema entsprechen) auf die Output-Row der Komponente.

5. Zu einem ersten Test verbinden wir die Komponente nun mit einer tLogRow und können uns so die ausgelesenen Werte anzeigen lassen.

Untersucht man den verwendeten Webservice genauer, stellt man fest, dass dieser nicht nur 1000 Einträge hat, wie man nach unserem Test vermuten könnte. Die Antwort des Services auf unseren Request wird allerdings vom Server standardmäßig beschnitten. Auch für dieses Problem gibt es aber eine simple Lösung mithilfe unserer Routine.

6. Wir zerlegen dazu unsere Anfrage in mehrere einzelne Anfragen, die immer nur einen Teil der Information abrufen. Ein angenehmer Nebeneffekt dieser Art von Auslese ist dabei außerdem, dass wir die zuerst ausgelesen Einträge direkt weiterverarbeiten (z.B. in eine Datei oder Datenbank schreiben) können, während die nächsten noch geladen werden. Hierzu benötigen wir ein paar Anpassungen im Code der tJavaFlex Komponente. Zuerst legen wir eine blockSize fest. Diese entspricht der Anzahl Einträge, die in einem Aufruf abgerufen werden sollen. Anschließend können wir (mithilfe des Wissens über die Gesamtzahl an Einträgen) ausrechnen, wie viele Blöcke in Größe der blockSize wir auslesen müssen. Durch eine weitere for-Schleife können wir nun das zuvor implementierte Auslesen blockweise realisieren.

7. Eine Ausführung unseres Jobs (statt die tLogRow zu verwenden, schreiben wir die Daten nun in eine csv-Datei, um die Konsolen-Anzeige nicht zu überlasten) zeigt uns, dass unser Job mit blockweiser Auslese auch für große Datenmengen gut funktioniert.

Auslese mit dem erweiterten Talend Job: 100 Zeilen

Ein Blick in unsere Ausgabedatei (und auf die dort gespeicherten IDs) zeigt uns außerdem, dass die Daten vollständig geladen wurden.

Wir konnten so also erfolgreich demonstrieren, dass auch OData-Services als Datenquelle für Talend dienen können und weiterhin zeigen, dass es sinnvoll sein kann, die einfache Bedienbarkeit von Talend mit der Mächtigkeit und Flexibilität von speziell geschriebenem Javacode zu kombinieren, z.B. im Hinblick auf die blockweise Auslese einer Datenquelle oder die erweiterte Konfiguration einer http-Verbindung.