RAP - Events
How can you actually create events in RAP and process them with ABAP? Here you can find out more about event-driven processing.
Table of contents
In this article we will look at how we can create events with our RAP BO, enrich them with data and process them locally in the system. We will look at various special features.
Introduction
In this article we want to extend our app for the Report Pattern and add an event. Event-driven architecture is becoming more and more the standard in development because it allows you to decouple applications from one another. Basically, an application or process creates an event, which is received by a queue and distributed to subscribers. In most cases, an event contains the key of the triggering instance, perhaps some additional information and the actual event, such as created, changed or deleted.
Event
In the first step, we have to define an event in our RAP object. We want to give the event a payload in order to be able to give the consumer some additional information.
Extension
The event should be triggered in the action for loading Excel. To do this, we want to give the event some additional information, which is passed on via the popup. In this case, we extend the abstract entity "ZBS_S_DRPExcelPopup" with a new field "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;
}
Parameters
In the next step, we need the additional parameters that we want to pass to the event. To do this, we create an abstract entity that maps the structure of the parameter. This gives us the option of passing on additional information in addition to the key. In principle, we can also read data about the entity in the event.
@EndUserText.label: 'Additional event data'
define abstract entity ZBS_S_DRPEventData
{
EventComment : abap.char(60);
LastEditor : abap.char(12);
}
Event definition
In the behavior we now define the event and also the parameters that we want to include when creating it. To do this, we extend the lowest layer of the behavior definition ZBS_R_DRPCurrency. In our main entity we adopt the event.
event AfterExcelLoad parameter ZBS_S_DRPEventData;
Triggering
The best practice is that we trigger the event when saving. The ADDITIONAL SAVE is available to us for this if we are completely in managed behavior. However, since we are already using an UNMANAGED SAVE, we can extend the method to trigger the event. However, if you try to trigger the event in an action in STRICT(2) mode, we will receive an error in our application.
We therefore have to adapt the behavior implementation of ZBP_BS_DRP_CURRENCY to trigger the event. Since we have no indicator in the data whether the Excel file has been loaded, we remember the key in a local buffer. Normally you could use the changed data here as a reference.
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.
In the next step, we fill the buffer if the action "LoadExcelContent" was successfully triggered and adopt the key.
INSERT ls_key INTO TABLE lcl_buffer=>gt_event.
In the SAVE_MODIFIED method we now trigger the event for the buffered keys. With the new statement RAISE ENTITY EVENT we specify our keys and parameters.
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.
The event is now created in the system and we can register to receive and process it. This allows us to outsource the subsequent processes or create interfaces for other developers.
Consumption
In this article, we want to react locally to the event and process it in the same system. We do not need an event broker or a binding, but can directly create a class that processes the event.
Class
To be able to process events, we create a new global class that contains the implementation of the events in the local part. Here we follow the behavior implementation pattern. After the addition FOR EVENTS OF we add our business object.
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.
In the next step we can implement the local class. To do this we create a local class, the name of the class does not matter here, but it must inherit from the class CL_ABAP_BEHAVIOR_EVENT_HANDLER. We would now create a separate method for each event.
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.
The name of the method is also irrelevant, but should remain descriptive. The name of the import parameter can also be freely chosen if naming conventions are required in your company. After the FOR comes the entity and then the corresponding event. Here you can also use the Content Assist (CTRL + SPACE) in Eclipse.
Test
In the next step we test the call of the event. To do this we can set a breakpoint in the method and execute the action in the UI.
After executing the action we first get the success popup that the data was loaded into the entity and processed. The debugger then starts in our Eclipse. If we look at the parameter, an event was delivered which contains the additional data for the event.
Logic
Finally, we want to react to the event by sending the current user an email with the current status of the data set and the comment. However, you should note that you are in the RAP transaction phases and are therefore subject to the rules.
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 the example, we start with MODIFY in CL_ABAP_TX. However, you do not have to set this phase; this is the default when the method is started. We can then prepare the email, read data via EML, and update other data. Once we have done this, we need to change the phase to SAVE to send the email. Now we can issue inserts, updates, and modify. However, we still cannot issue a COMMIT or a ROLLBACK, as this is controlled by RAP. Therefore, we use the SEND_ASYNC method to send the email.
We have built in a small special feature when preparing the email: here we create an image tag and add the attached image to the email as content. To do this, we convert the image to BASE64 and transfer it to the IMG tag with the correct MIME type. This means that the image appears within the email.
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.
Full example
All changes from this blog can be found in the following commit of the GitHub repository. In this commit we had to make changes to the SAVE_MODIFIED method because minor errors had crept in. You can find more details about this method in this article.
Conclusion
The implementation of events in RAP is very simple, but you should pay attention to the different RAP phases in order to achieve clean processing without many errors.
Source:
SAP Help - Business Events
SAP Help - Business Event Consumption