In diesem Teil lernen wir Router, Content Modifier, Content Enricher und etwas mehr Groovy Scripting kennen, während wir unseren Integration Flow weiter ausbauen
Hallo Technologie-Reisende!
Willkommen zum dritten Teil von Enterprise Integration mit SAP CPI. In den ersten beiden Teilen (lesen Sie den ersten und den zweiten Teil, falls noch nicht geschehen) haben wir unser Integrationsszenario erarbeitet, das Vorgehen in 7 Schritte aufgeteilt und außerdem die ersten drei Schritte der Lösung besprochen. Beim Aufbau unseres Integration Flow haben wir Message Events, Start Timer und einige Scheduling-Optionen, das Request Reply Shape, das Groovy Script Shape sowie General und Iterative Splitter kennengelernt.
In diesem Teil besprechen wir den 4. Schritt:
- Die Integration so konfigurieren, dass sie täglich läuft
- Die Liste der Produkte beziehen
- Für jedes Produkt in der Liste feststellen, ob eine Nachbestellung erforderlich ist
- Falls das Produkt nachbestellt werden muss, die Lieferanteninformationen beziehen
- Eine Liste der nachzubestellenden Produkte samt Lieferanteninformationen erstellen
- Die Liste in das CSV-Format konvertieren
- Die Liste per E-Mail an den Filialleiter senden
Bislang haben wir einzelne Produkte extrahiert und festgestellt, ob sie nachbestellt werden müssen oder nicht. Muss ein Produkt nachbestellt werden, setzen wir die Message Property needsReorder auf true. Andernfalls wird sie auf false gesetzt.
Schritt 4: Falls ein Produkt nachbestellt werden muss, die Lieferanteninformationen beziehen
(Verwendete technische Konzepte: Message Router, Content Modifier, Gather, Content Enricher)
Da wir nun wissen, welche Produkte nicht nachbestellt werden müssen, können wir diese Produkte guten Gewissens aus diesem Integration Flow aussteuern, denn sie brauchen keine weitere Verarbeitung. Um sie von den nachzubestellenden Produkten zu trennen, verwenden wir einen Message Router.
Sehr häufig begegnen uns Szenarien, in denen wir Nachrichten, die durch unsere Integration laufen, anhand einer Bedingung trennen und unterschiedlich behandeln wollen. Der Message Router ist ein Shape, mit dem Nachrichten anhand festgelegter Kriterien auf verschiedene Routen geschickt werden. Über Router können Sie hier nachlesen.
Um einen Message Router in unseren Integration Flow einzufügen, klicken Sie auf Message Routing->Router.
Platzieren Sie das Router-Shape zwischen dem Groovy Script Shape und dem Message End Shape.
Bevor wir verschiedene Routen anlegen, müssen wir entscheiden, wie viele Routen benötigt werden, welche Nachrichten über diese Routen geschickt werden und welche Operationen auf ihnen ausgeführt werden sollen. Wie bereits besprochen, legen wir zwei Routen an: eine für Produkte, die nachbestellt werden müssen, und eine für jene, bei denen das nicht der Fall ist. Welche Schritte für die nachzubestellenden Produkte nötig sind, wissen wir. Wir wissen auch, dass die Produkte ohne Nachbestellbedarf verworfen bzw. genullt werden können.
Um die Nachrichten für Produkte ohne Nachbestellbedarf zu nullen, verwenden wir ein Content Modifier Shape. Wie der Name schon sagt, erlaubt uns ein Content Modifier Shape, eine Nachricht zu verändern, indem wir ihren Body, ihre Headers und ihre Properties anpassen. In diesem Fall wollen wir den Message Body auf null setzen.
Um einen Content Modifier hinzuzufügen, klicken Sie auf Message Transformers->Content Modifier.
Platzieren Sie ihn zwischen dem Message Router und dem End Message Shape.
Klicken Sie nun auf den Content Modifier, wechseln Sie auf den Reiter 'Message Body' und setzen Sie den Wert von Body auf null.
Wenn Sie den Integration Flow an dieser Stelle speichern, sehen Sie Fehlermarkierungen an Shapes und Connectors:
Das liegt daran, dass wir für diese Route noch keine Route Condition angegeben haben und weil bislang nur eine Route vorhanden ist. Klicken Sie auf den Connector mit der Bezeichnung 'Route 1' und wählen Sie unter dem Reiter 'Processing' die Option 'Default Route'. Das bedeutet, dass alle Nachrichten diese Route nehmen müssen, sofern sie keiner anderen Route Condition entsprechen. Der nächste logische Schritt besteht darin, eine Route für die nachzubestellenden Produkte anzulegen.
Wir hatten die Liste aller Produkte für die Bewertung in einzelne Produkte aufgeteilt, müssen dem Filialleiter am Ende jedoch eine konsolidierte Liste schicken. Dafür verwenden wir ein Gather Shape. Das Gather Shape wird verwendet, um Nachrichten auf einer oder mehreren Routen so einzusammeln, wie es in seiner Konfiguration festgelegt ist.
Klicken Sie auf Message Routing->Gather und platzieren Sie es so auf dem Canvas:
Klicken Sie auf das Router-Shape und ziehen Sie einen Connector zu Gather. Er wird automatisch als 'Route 2' bezeichnet.
Klicken Sie auf Route 2 und setzen Sie unter dem Reiter Processing die Condition auf: ${property.needsReorder} = 'true'
Damit haben wir festgelegt, dass eine Nachricht dieser Route folgen muss, wenn die Message Property needsReorder auf true gesetzt ist, andernfalls wird die Default Route genommen. Zudem muss jede Route irgendwo enden. Ziehen Sie einen Connector vom Gather Shape zum Message End Shape, um die Route abzuschließen.
Nun ändern wir den Connector zwischen Content Modifier und End Shape so, dass er stattdessen mit dem Gather Shape verbunden ist. Warum wir das tun sollten, fragen Sie? Logisch betrachtet hätten wir es auch so lassen können. Wir haben die Message Bodies für Produkte ohne Nachbestellbedarf genullt und den Prozess für diese Nachrichten danach beendet. Das hätte allerdings dazu geführt, dass mehrere Nachrichten das End Shape erreichen. Angenommen, 80 Produkte müssten nicht nachbestellt werden: Dann würden neben der konsolidierten Liste für die nachzubestellenden Produkte 80 Nachrichten am End Shape ankommen. Das hätte zu Problemen beim Nachverfolgen dieser Nachrichten führen können, oder schlimmer noch, zu 80 unnötigen E-Mails!
Wir müssen das Gather Shape konfigurieren und sein Incoming Format sowie den Aggregation Algorithm festlegen. Wir wissen, dass wir Nachrichten mit Produktdetails und auch einige Nachrichten mit geleertem Body einsammeln wollen und dass sie zu einer einzigen Nachricht verkettet werden sollen. Deshalb setzen wir das Incoming Format auf 'Plain Text' und den Aggregation Algorithm auf 'Concatenate'. Sie können je nach Relevanz und Vorliebe auch andere Optionen nutzen. Mehr über das Gather Shape lesen Sie hier.
Auch wenn wir ein Gather Shape hinzugefügt haben, fehlen noch die Shapes für die Operationen, die auf den Nachrichten der Route 2 vor dem Einsammeln ausgeführt werden sollen. Ziel ist es, jeder dieser Nachrichten die Lieferanteninformationen hinzuzufügen. Das lässt sich in die folgenden Aufgaben unterteilen:
- Produkt-ID aus der Nachricht beziehen
- Lieferanteninformationen anhand der Produkt-ID beziehen
- Message Bodies bilden, die sich für die Verkettung eignen
1. Produkt-ID aus der Nachricht beziehen
Dafür verwenden wir einen Content Modifier. Wie zuvor besprochen, dient ein Content Modifier Shape dazu, Message Body, Properties und Headers zu verändern. Wir haben ihn bereits genutzt, um den Message Body auf null zu setzen. Nun extrahieren wir die Produkt-ID aus dem Message Body und legen sie in den Message Properties ab.
Fügen Sie einen Content Modifier zwischen dem Router und dem Gather Shape ein.
Wir verwenden einen XPath-Ausdruck, um die Produkt-ID aus dem Message Body zu extrahieren: /Products/Product/SupplierID
2. Lieferanteninformationen anhand der Produkt-ID beziehen
Dafür verwenden wir ein Shape namens 'Content Enricher', das Daten aus einer externen Quelle (Lookup Message) bezieht und sie den vorhandenen Daten (Original Message) im Body hinzufügt. In unserem Fall wollen wir den Produktdetails, die wir aktuell im Message Body haben, die Lieferanteninformationen hinzufügen.
Um einen Content Enricher hinzuzufügen, klicken Sie auf Call->External Call->Content Enricher und platzieren ihn zwischen dem Content Modifier und dem Gather Shape:
Da wir Daten von einer entfernten Quelle beziehen, fügen wir einen Receiver hinzu, genau wie im ersten Schritt des Integration Flow, in dem wir die Produktinformationen bezogen haben. Fügen Sie einen Receiver und anschließend einen OData-Adapter vom Receiver zum Content Enricher hinzu.
Klicken Sie auf den OData-Adapter, um die Verbindung zu konfigurieren. Die Konfiguration ähnelt stark Schritt 2 aus dem ersten Teil dieser Blogserie, nur beziehen wir die Daten statt aus 'Products' aus 'Suppliers'.
Nun wählen wir den Aggregation Algorithm für den Content Enricher. Es gibt zwei Optionen für den Aggregation Algorithm:
- Combine: Bei dieser Option wird die Lookup Message an die Original Message angehängt.
- Enrich: Bei dieser Option wird die Lookup Message über ein gemeinsames Feld mit der Original Message zusammengeführt.
In unserem Szenario wollen wir die Lieferantendetails über die Supplier ID als gemeinsames Feld in die Produktinformationen einfügen, daher wählen wir Enrich als Aggregation Algorithm. Mehr über den Content Enricher lesen Sie hier.
Sowohl für die Enrichment Message als auch für die Original Message müssen wir den Path to Node angeben, unter dem die Entitätsdaten liegen, in unserem Fall also die Produktdetails und die Lieferantendetails. Das Key Element ist der Name des Elements, das für die Zusammenführung übereinstimmen muss. Die Konfiguration sieht so aus:
An diesem Punkt haben wir die Lieferanteninformationen in die Produktinformationen eingefügt, und die angereicherten Daten sehen so aus:
<?xml version='1.0' encoding='UTF-8'?>
<Products>
<Product>
<CategoryID>8</CategoryID>
<Discontinued>false</Discontinued>
<SupplierID>13</SupplierID>
<Supplier>
<SupplierID>13</SupplierID>
<CompanyName>Nord-Ost-Fisch Handelsgesellschaft mbH</CompanyName>
<Address>Frahmredder 112a</Address>
<Phone>(04721) 8713</Phone>
<Region/>
<PostalCode>27478</PostalCode>
<Country>Germany</Country>
<City>Cuxhaven</City>
<ContactName>Sven Petersen</ContactName>
<ContactTitle>Coordinator Foreign Markets</ContactTitle>
</Supplier>
<UnitPrice>25.8900</UnitPrice>
<ProductName>Nord-Ost Matjeshering</ProductName>
<QuantityPerUnit>10 - 200 g glasses</QuantityPerUnit>
<UnitsOnOrder>0</UnitsOnOrder>
<ProductID>30</ProductID>
<ReorderLevel>15</ReorderLevel>
<UnitsInStock>10</UnitsInStock>
</Product>
</Products>
3. Message Bodies bilden, die sich für die Verkettung eignen
Wir haben die Produktdetails samt Lieferantendetails, aber das resultierende XML liegt verschachtelt vor: Die Lieferantendetails befinden sich nicht auf derselben Hierarchieebene wie die übrigen Produktdetails. Es ist wichtig, alle Daten auf derselben Ebene zu haben, damit sich die Nachricht sauber in das CSV-Format konvertieren lässt. Daher müssen die folgenden Aufgaben auf diesen Daten ausgeführt werden:
- Alle relevanten Daten in der XML-Datei auf dieselbe Hierarchieebene bringen.
- Doppelte Daten entfernen, sofern vorhanden.
Dafür verwenden wir das folgende Groovy Script:
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import groovy.util.XmlSlurper ;
import groovy.xml.XmlUtil;
def Message processData(Message message) {
//Body
def body = message.getBody(java.lang.String) as String;
def rootNode= new XmlSlurper().parseText(body);
def product=rootNode.Product;
product.SupplierID.replaceNode{
};
product.appendNode(product.Supplier.SupplierID);
product.appendNode(product.Supplier.ContactName);
product.appendNode(product.Supplier.ContactTitle);
product.appendNode(product.Supplier.CompanyName);
product.appendNode(product.Supplier.Address);
product.appendNode(product.Supplier.Phone);
product.appendNode(product.Supplier.PostalCode);
product.appendNode(product.Supplier.Country);
product.Supplier.replaceNode{};
def flatXML=XmlUtil.serialize(product);
flatXML=flatXML-"""<?xml version="1.0" encoding="UTF-8"?>""";
flatXML=flatXML-"""<?xml version='1.0' encoding='UTF-8'?>""";
message.setBody(flatXML);
return message;
}
In diesem Script lesen wir zunächst mit XMLSlurper den XML-Body, um die relevanten Lieferanteninformationen aus dem beim Enrichment hinzugefügten Supplier-Node zu extrahieren. Anschließend fügen wir diese Informationen dem Product-Node hinzu, sodass alle benötigten Informationen auf derselben Hierarchieebene liegen. Das geschieht über die Methode appendNode des XMLSlurper. Sobald wir die benötigten Informationen im gewünschten Format haben, entfernen wir die überflüssigen Datenknoten mit replaceNode.
Wenn Sie sich das Script ansehen, werden Sie feststellen, dass wir den Node mit der Supplier Id entfernt haben, bevor wir die Supplier Data angehängt haben. Damit vermeiden wir redundante Daten. Sie fragen sich vielleicht, warum wir die Supplier-ID-Daten überhaupt hinzugefügt haben, wenn sie ohnehin schon vorhanden waren. Wenn wir mit dem XMLSlurper einen Node anhängen, werden die Daten am Ende eingefügt. In unserem Fall werden also alle Lieferanteninformationen ans Ende des Product-Node gesetzt, sodass diese Daten beisammen bleiben. Wenn wir diese Daten später in das CSV-Format konvertieren, ist es sinnvoll, die Lieferanteninformationen an einer Stelle zu haben, statt sie über die Produktinformationen verstreut vorzufinden. Bitte beachten Sie, dass dies nicht der einzige Weg zu diesem Ergebnis ist. Die anderen Wege besprechen wir zu einem späteren Zeitpunkt.
Das war's! Wir legen an dieser Stelle eine Pause ein, damit Sie die behandelten Themen vollständig verstehen und verinnerlichen können. In diesem Blogbeitrag haben wir Router, Content Modifier, Content Enricher und etwas mehr Groovy Scripting kennengelernt. Im nächsten Blogbeitrag lernen wir unter anderem die XML-zu-CSV-Konvertierung und den Mail-Adapter kennen und schließen damit unsere Reise durch die Enterprise Integration mit SAP CPI ab. Bis dahin: auf Wiedersehen und viel Freude beim Lernen!