RAP - Events
Wie kannst du eigentlich Events in RAP erzeugen und mit ABAP verarbeiten? Hier erfährst du mehr zur eventgetriebenen Verarbeitung.
Inhaltsverzeichnis
In diesem Artikel schauen wir an, wie wir mit unserem RAP BO Events erzeugen können, diese mit Daten anreichern und lokal im System verarbeiten. Dabei gehen wir auf verschiedene Besonderheiten ein.
Einleitung
In diesem Artikel wollen wir unsere App für das Report Pattern erweitern und um ein Event erweitern. Eventgetriebene Architektur wird immer mehr zum Standard in der Entwicklung, da du dadurch Anwendungen voneinander entkoppeln kannst. Im Grunde erzeugt eine Anwendung oder ein Prozess ein Event, welches durch eine Queue aufgenommen wird und an Subscriber verteilt wird. Ein Event enthält in den meisten Fällen den Schlüssel der auslösenden Instanz, vielleicht ein paar Zusatzinformationen und das eigentliche Event, wie zum Beispiel angelegt, geändert oder gelöscht.
Event
Im ersten Schritt müssen wir in unserem RAP Objekt ein Event definieren. Dem Event wollen wir eine Payload mitgeben, um zum Consumer einige Zusatzinformationen geben zu können.
Erweiterung
Das Event soll in der Aktion zum Laden des Excels ausgelöst werden. Dazu wollen wir dem Event noch einige Zusatzinformationen mitgeben, die über das Popup übergeben werden. In diesem Fall erweitern wie die abstrakte Entität "ZBS_S_DRPExcelPopup" um ein neues Feld "EventComment".
@EndUserText.label: 'Excel Popup'
define abstract entity ZBS_S_DRPExcelPopup
{
@EndUserText.label: 'Comment'
EventComment : abap.char(60);
@EndUserText.label: 'Test run'
TestRun : abap_boolean;
}
Parameter
Dazu benötigen wir im nächsten Schritt die zusätzlichen Parameter, die wir dem Event mitgeben wollen. Dazu legen wir eine abstrakte Entität an, die die Struktur des Parameters abbildet. Damit haben wir die Möglichkeit, neben dem Schlüssel auch noch zusätzliche Informationen zu übergeben. Grundsätzlich können wir Daten zur Entität auch im Event nachlesen.
@EndUserText.label: 'Additional event data'
define abstract entity ZBS_S_DRPEventData
{
EventComment : abap.char(60);
LastEditor : abap.char(12);
}
Eventdefinition
Im Verhalten definieren wir nun das Event und auch die Parameter, die wir bei der Erzeugung mitgeben wollen. Dazu erweitern wir die unterste Schicht der Verhaltensdefinition ZBS_R_DRPCurrency. In unserer Hauptentität übernehmen wir das Event.
event AfterExcelLoad parameter ZBS_S_DRPEventData;
Auslösen
Die Best-Practice sieht vor, dass wir das Event beim Speichern auslösen. Dazu steht uns der ADDITIONAL SAVE zur Verfügung, falls wir komplett im Managed Verhalten sind. Da wir aber bereits einen UNMANAGED SAVE verwenden, können wir die Methode erweitern, um das Event auszulösen. Versuchst du allerdings im STRICT(2) Modus das Event in einer Aktion auszulösen, erhalten wir in unserer Anwendung einen Fehler.
Wir müssen also die Verhaltensimplementierung von ZBP_BS_DRP_CURRENCY anpassen, um das Event auszulösen. Da wir keinen Indikator in den Daten haben, ob die Excel Datei geladen wurde, merken wir uns den Schlüssel in einem lokalen Puffer. Normalerweise könntest du hier die veränderten Daten als Referenz benutzen.
CLASS lcl_buffer DEFINITION.
PUBLIC SECTION.
TYPES tt_keys TYPE TABLE FOR ACTION IMPORT zbs_r_drpcurrencycurrency~loadexcelcontent.
CLASS-DATA gt_event TYPE tt_keys.
ENDCLASS.
Im nächsten Schritt befüllen wir den Puffer, wenn die Aktion "LoadExcelContent" erfolgreich ausgelöst wurde und übernehmen den Schlüssel.
INSERT ls_key INTO TABLE lcl_buffer=>gt_event.
In der Methode SAVE_MODIFIED lösen wir nun das Event für die gepufferten Schlüssel aus. Mit dem neuen Statement RAISE ENTITY EVENT geben wir unsere Schlüssel und Parameter mit.
LOOP AT lcl_buffer=>gt_event INTO DATA(ls_event).
RAISE ENTITY EVENT ZBS_R_DRPCurrency~AfterExcelLoad
FROM VALUE #( ( %key = ls_event-%key
%param = VALUE #( EventComment = ls_event-%param-EventComment
LastEditor = cl_abap_context_info=>get_user_technical_name( ) ) ) ).
ENDLOOP.
Das Event wird nun im System erzeugt und wir können uns darauf registrieren, um es zu empfangen und zu verarbeiten. Damit können wir die nachfolgenden Prozesse auslagern oder Schnittstellen für andere Entwickler schaffen.
Konsumierung
In diesem Artikel wollen wir lokal auf das Event reagieren und es im selben System verarbeiten. Dabei benötigen wir keinen Event Broker oder ein Binding, sondern können direkt eine Klasse anlegen, welches das Event verarbeitet.
Klasse
Um Events verarbeiten zu können, legen wir eine neue globale Klasse an, die im lokalen Teil die Implementierung der Events beinhaltet. Hier folgen wir dem Pattern der Verhaltensimplementierung. Hinter dem Zusatz FOR EVENTS OF ergänzen wir unser Business Objekt.
CLASS zcl_bs_drp_event_consumption DEFINITION
PUBLIC ABSTRACT FINAL
FOR EVENTS OF ZBS_R_DRPCurrency.
PUBLIC SECTION.
ENDCLASS.
CLASS zcl_bs_drp_event_consumption IMPLEMENTATION.
ENDCLASS.
Im nächsten Schritt können wir die lokale Klasse implementieren. Dazu legen wir eine lokale Klasse an, der Name der Klasse ist hier egal, sie muss allerdings von der Klasse CL_ABAP_BEHAVIOR_EVENT_HANDLER erben. Für jedes Event würden wir nun eine eigene Methode anlegen.
CLASS lcl_local_events DEFINITION INHERITING FROM cl_abap_behavior_event_handler.
PRIVATE SECTION.
METHODS after_excel_load FOR ENTITY EVENT it_parameters FOR Currency~AfterExcelLoad.
ENDCLASS.
CLASS lcl_local_events IMPLEMENTATION.
METHOD after_excel_load.
IF 0 = 0.
ENDIF.
ENDMETHOD.
ENDCLASS.
Der Name der Methode ist ebenfalls egal, sollte aber beschreibend bleiben. Der Name des Import Parameters kann ebenfalls frei gewählt werden, wenn bei dir im Unternehmen noch Namenskonventionen verlangt werden. Nach dem FOR kommt zuerst einmal die Entität und dann das entsprechende Event. Hier kannst du in Eclipse auch den Content Assist (STRG + LEER) verwenden.
Test
Im nächsten Schritt testen wir einmal den Aufruf des Events, dazu können wir einen Breakpoint in die Methode setzen und die Aktion im UI ausführen.
Nach der Ausführung der Aktion erhalten wir zuerst einmal das Erfolgspopup, dass die Daten in die Entität geladen und verarbeitet wurden. Im Anschluss startet in unserem Eclipse der Debugger. Schauen wir uns den Parameter an, wurde ein Event zugestellt, welches die zusätzlichen Daten für das Event enthält.
Logik
Zum Abschluss wollen wir auf das Event reagieren, in dem wir dem aktuellen User eine E-Mail zusenden mit dem aktuellen Stand des Datensatzes und dem Kommentar. Du solltest allerdings beachten, dass du dich in den RAP Transaktionsphasen befindest und damit den Regeln unterliegst.
cl_abap_tx=>modify( ).
LOOP AT it_parameters INTO DATA(ls_parameter).
send_mail_to_receiver( ls_parameter ).
ENDLOOP.
cl_abap_tx=>save( ).
LOOP AT mt_mails INTO DATA(lo_mail).
lo_mail->send_async( ).
ENDLOOP.
In dem Beispiel starten wir mit MODIFY in CL_ABAP_TX. Diese Phase musst du allerdings nicht setzen, diese ist der Standard, wenn die Methode gestartet wird. Im Anschluss können wir die Mail vorbereiten, per EML Daten nachlesen und weitere Daten aktualisieren. Sind wir so weit damit fertig, müssen wir für den Versand der E-Mail die Phase auf SAVE wechseln. Nun können wir Inserts, Updates und Modifys absetzen. Allerdings können wir immer noch keinen COMMIT oder einen ROLLBACK absetzen, da dieser durch RAP gesteuert wird. Daher verwenden wir die Methode SEND_ASYNC, um die Mail zu versenden.
Eine kleine Besonderheit haben wir noch bei der Aufbereitung der E-Mail eingebaut, hier erzeugen wir ein Image Tag und hängen das angehangene Bild in die Mail als Content ein. Dazu konvertieren wir das Bild nach BASE64 und übernehmen es mit dem richtigen MIME-Type in das IMG Tag. Damit erscheint das Bild innerhalb der Mail.
READ ENTITIES OF ZBS_R_DRPCurrency
ENTITY Currency
ALL FIELDS WITH VALUE #( ( Currency = is_parameter-Currency ) )
RESULT DATA(lt_currency).
DATA(ls_currency) = VALUE #( lt_currency[ 1 ] OPTIONAL ).
DATA(ld_base64) = cl_web_http_utility=>encode_x_base64( ls_currency-PictureAttachement ).
DATA(ld_message) = |<h3>{ is_parameter-EventComment }</h3>|.
ld_message &&= |<img src="data:{ ls_currency-PictureMimetype };base64, { ld_base64 }" style="height:200px; width: 200px" />|.
ld_message &&= |<p>{ ls_currency-CurrencyComment }</p>|.
RETURN ld_message.
Vollständiges Beispiel
Alle Änderungen aus diesem Blog findest du im folgenden Commit des GitHub Repository. Bei diesem Commit mussten wir noch Änderungen vornehmen bei der SAVE_MODIFIED Methode, da sich kleinere Fehler eingeschlichen hatten. Mehr Details zu dieser Methode findest du in diesem Artikel.
Fazit
Die Implementierung von Events in RAP funktioniert sehr einfach, allerdings solltest du auf die verschiedenen RAP-Phasen achten, um eine saubere Verarbeitung, ohne viele Fehler, zu erreichen.
Quelle:
SAP Help - Business Events
SAP Help - Business Event Consumption