Moin! Wir kehren heute mal wieder zur SAP Cloud Foundry zurück um unseren OData Service mit Custom Logic zu erweitern. Benjamin hatte hier ja schon beschrieben, wie wir mit dem SAP Cloud Application Programming Models und Core Data Services (CDS) einfach einen OData Service erstellen können. Wenn Du den Beitrag noch nicht kennst, dann lauf mal schnell rüber und lerne dort die Grundlagen, auf denen wir diesmal aufbauen. Ich warte hier so lange.
Wieder da? Sehr schön, dann fangen wir mal an!
Wie schon im letzten Beitrag bleiben wir diesmal bei DB und Service und lassen die UI erstmal außen vor. Stattdessen beschäftigen wir uns damit, wie wir unsere Services mit Custom Logic erweitern können, wenn uns die generierten CDS nicht reichen. Dazu gebe ich zwei Beispiele, wie wir eigene Logik in unseren OData Service einbauen können.
[0]: Vorraussetzungen
Dieser Blog geht davon aus, dass du den ersten Teil des Beitrags durchgearbeitet hast. Außerdem solltest du zumindest über grundlegende Java-Kenntnisse verfügen. Das trifft auf dich zu? Dann können wir ja gleich loslegen!
[1]: Hooks
CDS stellen uns eine Reihe von “Hooks” zur Verfügung, für die wir eigene Handler-Methoden schreiben können, die dann je nach Hook zu unterschiedlichen Zeitpunkten im Ablauf der Transaktion durchlaufen werden. Wir beschränken uns hier auf die folgenden Hooks, die wir für jede Operation für jede Entität ausbilden können.
- Before<Operation> Hook –> wird vor Ausführung der Operation (CRUD) durchlaufen, Annotation @Before<Operation>
- After<Operation> Hook –> wird nach der Ausführung der Operation (aber noch vor Commit/Rollback) durchlaufen, Annotation @After<Operation>
Die Web IDE stellt uns hierfür einen hilfreichen Wizard zur Verfügung: Wir klicken mit der rechten Maustaste auf unseren srv-Ordner und wählen New -> Entity Operation Hooks aus.
Im Wizard wählen wir die gewünschten Entitäten aus und drücken auf Next und dann Finish. Und schwups, legt uns die Web IDE an der richtigen Stelle eine Java-Klasse mit auskommentierten Methoden-Stubs an:
Wir sehen hier, dass die Methoden alle schon die nötigen Annotationen haben und diese die Entität und unseren Service-Namen als Parameter haben. Um eine Methode zu nutzen, kommentieren wir diese und alle benötigten Importe einfach ein. Lass deiner Fantasie vollen lauf, was die Implementierung deiner Custom Logic angeht! Bei mir ist das einfach eine Validierung, dass die Menge an OrderItems größer Null ist. Zugegebenermaßen nicht sehr einfallsreich, aber dir fällt da sicher mehr ein. Zur Anregung findest du hier die Javadocs für das SAP Cloud Application Programming Model.
@BeforeCreate(entity = "OrderItems", serviceName = "CatalogService") public BeforeCreateResponse beforeCreateOrderItems(CreateRequest req, ExtensionHelper helper) { EntityData data = req.getData(); if (Integer.parseInt(data.getElementValue("OI_Quantity").toString()) < 1) { return BeforeCreateResponse.setError(ErrorResponse.getBuilder() .setMessage("Quantity must be greater 0").response()); } else { return BeforeCreateResponse.setSuccess().response(); } }
Die Möglichen Return-Statements sind uns in den Stubs dabei sogar schon angelegt, wir wählen einfach aus, was wir wo brauchen und passen den Fehlertext an.
[2]: Generic Operation Overrides
Bisher haben wir gesehen, wie wir uns vor und nach einer Operation in die Transaktion einhängen können und z.B. Validierungen druchführen können. Aber manchmal ist es nötig, dass wir die Operation selbst an unsere eigenen Wünsche anpassen. Hierzu können wir die generischen Operationen überschreiben, indem wir eine entsprechend annotierte Methode anlegen und implementieren. Auch hierfür stellt uns die Web IDE freundlicherweise einen Wizard zur Verfügung, auch wenn wir diesmal noch etwas mehr Hand anlegen müssen.
Wieder klicken wir mit der rechten Maustaste auf den srv-Ordner. Diesmal wählen wir aber New -> Custom Entity Operations aus. Wieder wählen wir die gewünschten Entitäten aus und drücken Next sowie Finish.
Die generierte Datei stellt uns wieder auskommentierte Funktionen zur Verfügung, die auch annotiert sind. Allerdings fehlt uns in der Signatur der Methoden noch ein Input-Parameter, der ExtensionHelper. Dies habe ich hier für die Methode zum Überschreiben der read-Operation hinzugefügt.
Jetzt geht es wieder daran, dass wir unsere gewünschte Logik für die Operation implementieren. Ich bleibe wieder bei einem einfachen Beispiel: Ich möchte das Produkt nur dann zurückgeben, wenn es auch vorrätig ist.
@Read(entity = "Products", serviceName = "CatalogService") public ReadResponse readProducts(ReadRequest req, ExtensionHelper extensionHelper) { DataSourceHandler handler = extensionHelper.getHandler(); EntityMetadata entityMetadata = req.getEntityMetadata(); try { EntityData entityData = handler.executeRead(entityMetadata.getName(), req.getKeys(), entityMetadata.getFlattenedElementNames()); if (Integer.parseInt(entityData.getElementValue("P_UnitsInStock").toString()) < 1) { ErrorResponse errorResponse = ErrorResponse.getBuilder() .setMessage("Requested product not available or not in stock.") .setStatusCode(404).response(); return ReadResponse.setError(errorResponse); } else { return ReadResponse.setSuccess().setData(entityData).response(); } } catch (DatasourceException ex) { ErrorResponse er = ErrorResponse.getBuilder().setMessage(ex.getMessage()) .setStatusCode(404).response(); return ReadResponse.setError(er); } }
Wenn wir uns die Methode nun anschauen, dann sehen wir, dass wir uns neben dem Handler, der die read-Operation zur Verfügung stellt, noch die Enititäts-Metadaten aus dem Request holen. Dies ist nötig, da wir für den Handler den Namen der Entität, sowie die flache Form von komplexen oder strukturierten Elementen benötigen. Im Fehlerfall oder wenn das Produkt nicht vorrätig ist, geben wir eine Fehlermeldung zurück, ansonsten die Daten, die wir gelesen haben.
Nun machen wir noch ein Build und starten unseren Java Service. Zu einem späteren Zeitpunkt schauen wir uns noch ein paar andere Möglichkeiten an, wie wir unser Projekt weiterentwickeln können, aber jetzt ist erst mal Kaffeezeit!
Du interessierst dich für Fiori und hast Lust coole Projekte mit uns zu machen? Wir suchen dich! Schau doch mal in unserer Stellenbeschreibung vorbei.
Daniel Gent sagt:
Danke für das nützliche Tutorial. Ich versuche sowas ähnliches zu machen, allerdings sind bei mir in der WebIde die Punkte “Entity Operation Hooks” und “Custom Entity Operations” ausgegraut. Ist dieses Feature evtl. schon wieder verschwunden, oder fehlt meinem Java-Projekt evtl. irgendetwas?