Raising and Handling Business Events

Objectives

After completing this lesson, you will be able to:
  • Describe Business Events.
  • Define and raise business events in RAP.
  • Handle business events locally.

Business Events

A visual representation of an event-driven communication process

Event-driven communication is a method of interaction between components or systems where messages, called events, are sent to signal the occurrence of specific actions or changes. Depending on the framework, event-driven communication can be synchronous or asynchronous. In synchronous communication, the event producer waits until the event has been consumed by all registered consumers. In asynchronous communication, the event producer does not wait after sending the event.

Event driven communication consists of three parts: The event producer, the event consumer, and the event channel.

Event Producer

An Event Producer raises an event when something significant happens (for example, a new flight travel is created). The producer does not know or care if there are any consumers for the event and never receives any feedback.

Event Consumers

Event Consumers listen for and react to specific events (for example, updating logs, sending notifications). The consumers must know the event producer and they actively register to receive the events they are interested in.

Event Channel

An event channel is the medium through which events are transmitted (for example, a message broker or an event bus). The channel administers the registrations, receives the events, and distributes them to the registered consumers.

In essence, event-driven communication is like a publish-subscribe model, where event producers publish events to the event channel, and event consumers subscribe to the types of events they are interested in. It allows for a dynamic and decoupled communication pattern.

A representation of business events in RAP

RAP supports event-driven communication natively by enabling business objects to publish and trigger events. These events are called Business Events to distinguish them from the events of other event-based communication concepts in ABAP.

Business events usually represent a significant change of state of the RAP business object, or of one of its children, that is relevant for follow-up processes. For example, when a new travel is created, you can enable consuming applications to trigger more actions with the help of business events.

Business event consumption is always asynchronous and supports internal and external consumers. To make a business event visible for external consumers, it is necessary to create an Event Binding. Event bindings are ABAP repository objects that enable the developer to map the relevant information to be sent to a business event to the SAP event infrastructure.

Similarly, ABAP code must define an Event Consumption Model to be able to receive and consume events from external applications.

Note

In this course, we only cover the local consumption of business events. For details on Event Bindings and Event Consumption Models, see here and here.

RAP Business Objects as Event Producers

Business event definition and raising

You define a RAP business event using the event keyword inside the behavior body of a RAP BO entity. You can define events in any entity of a managed or unmanaged RAP BO.

Optionally, you can define an event parameter to specify the information that is passed to the event consumers.

Note

We discuss event parameters later in this course.

A RAP business event must be raised using the EML statement RAISE ENTITY EVENT … FROM … . Unlike other EML statements, the RAISE ENTITY EVENT statement can only be used in ABAP behavior pools.

Note

Do not confuse the RAISE ENTITY EVENT statement with the ordinary RAISE EVENT statement, which is used to raise events in the context of object-oriented programming in ABAP.

The RAISE ENTITY EVENT Statement

When you implement a RAISE ENTITY EVENT statement, you can only raise events that are defined in the behavior definition for the same RAP BO. The event is specified after the event keyword, using the global name of the entity and the name of the event, separated by a tilde sign (~). Entity alias names are not supported at this position.

Technically, you can use the RAISE ENTITY EVENT statement anywhere in the behavior implementation. However, it is recommended that RAP business events are raised during the saver methods save or save_modified. In managed RAP BOs, it is recommended that you specify an additional save and raise the RAP business event in this additional save implementation.

The data object or expression after the FROM addition has to be based on the derived data type TABLE FOR EVENT <entity_name>~<event_name>. The line type of this derived data type consists of at least the primary key fields of the entity. In the example, these are the fields agencyid and travelid. If the event is defined with an event parameter, there are more components. You can address the key fields through the %key component group.

Caution

The FOR EVENT types do not contain a %is_draft component or a %tky component group, even if the BO is draft enabled. Therefore, it is not possible to raise events specifically for draft entities.

Hint

It is not necessary to explicitly define the data object. As usual in EML, you can also use a VALUE #( ) expression directly after FROM.

Local Consumption of Business Events

Business event handler definition

To consume a business event locally, that is, in the system where it is raised, you have to define a business event handler method. This method is then called locally and processed asynchronously.

For the definition of business event handler methods, the following restrictions and prerequisites apply:

  • You can only define business event handler methods in local classes.
  • The local class needs to be part of a global class (= Class Pool).
  • The global class is defined with the FOR EVENTS OF addition, followed by the name of the business object that contains the event definition.

    Note

    Global classes with this addition are referred to as RAP event handler classes.

  • The local class needs to inherit from global class CL_ABAP_BEHAVIOR_EVENT_HANDLER.
  • The business event handler method is defined in the private section.

    Note

    Classes inheriting from CL_ABAP_BEHAVIOR_EVENT_HANDLER can only have a private section. It is forbidden to define a public section or a protected section.

The syntax for defining a business event handler method follows the pattern METHODS <method_name> FOR ENTITY EVENT IMPORTING <parameter_name> FOR <entity_name>~<event_name>. The values for <entity_name> and <event_name> identify the event that you want to handle.

Note

At this position, it is possible to use the entity alias name instead of the full, globally unique entity name.

The other names (<method_name> and <parameter_name>) you can choose freely.

Caution

It is mandatory to specify the importing parameter, even if the event does not define an event parameter. In this case, the importing parameter contains only the keys of the affected BO entity instances.

Hint

Even though specifying the importing parameter is mandatory, you can nevertheless omit the IMPORTING keyword. It results in the following short form: METHODS <method_name> FOR ENTITY EVENT <parameter_name> FOR <entity_name>~<event_name>.

Define, Raise, and Handle a Business Event

In this exercise, you define an event in the behavior definition of your business object and raise it in the behavior implementation during the creation of new flight travels. You then handle the event locally in an ABAP class.

Note

In this exercise, replace ## with your group number.

Solution

Repository Object TypeRepository Object ID
CDS Behavior Definition (Model)/LRN/437G_R_TRAVEL
ABAP Class (Behavior Pool)/LRN/BP_437G_R_TRAVEL
ABAP Class (Event Handler)/LRN/CL_437G_HANDLER

Task 1: Define and Raise the Event

Edit the behavior definition on data model level Z##_R_TRAVEL. In the behavior for the root entity, define an event (suggested name: TravelCreated). Raise the event in an additional save implementation, when one or more new flight travels are created.

Steps

  1. Edit your behavior definition on data model level Z##_R_TRAVEL and locate the define behavior statement for the root entity Z##_R_Travel.

    1. For example, you can press Ctrl + F to invoke a search dialog where you search for R_Travel.

  2. For the root entity, define an event (suggested name: TravelCreated).

    1. Somewhere in the behavior definition, for example after the definition of the cancel_travel action, add the following code:

      Code Snippet
      1
      event TravelCreated;
  3. Enable additional save logic for the root entity.

    1. At the end of the define behavior statement for the root entity, add the with additional save addition as follows:

      Code Snippet
      12345678910
      define behavior for Z##_R_Travel alias Travel persistent table z##_travel draft table z##_travel_d lock master total etag ChangedAt authorization master ( instance ) etag master LocChangedAt early numbering with additional save {
  4. Activate the behavior definition.

    1. Press Ctrl + F3 to activate the development object.

  5. Navigate to the implementation of the save_modified method.

    Note

    In our case, this method already exists because the travel item behavior contains the with unmanaged save addition. Otherwise, you have to use a quick fix to generate the method and, if necessary, the saver class.
    1. For example, scroll up to the managed implementation in class statement. Place the cursor on zbp_##_r_travel and press F3.

    2. Choose the Local Types tab.

    3. Scroll down to the METHOD save_modified. statement.

  6. At the end of the save_modified method implementation, check if there are any new flight travels.

    Hint

    Evaluate the travel component of the create importing parameter.
    1. Before the next ENDMETHOD statement, add the following code:

      Code Snippet
      123
      IF create-travel IS NOT INITIAL. ENDIF.
  7. If there are newly created flight travels, raise the TravelCreated event, passing the key values from the create-travel parameter.

    Hint

    A CORRESPONDING expression automatically passes the key fields.
    1. Adjust the code as follows:

      Code Snippet
      1234
      IF create-travel IS NOT INITIAL. RAISE ENTITY EVENT Z##_R_Travel~TravelCreated FROM CORRESPONDING #( create-travel ) . ENDIF.
  8. Activate the behavior implementation.

    1. Press Ctrl + F3 to activate the development object.

Task 2: Handle the Event

Create a new global class (suggested name: ZCL_##_HANDLER) to handle the TravelCreated event you defined in the previous task. Implement the handler method with a MODIFY ENTITIES statement that uses the business object interface /LRN/437_I_TRAVELLOG to create a log entry for the new flight travels.

Note

Make sure to populate the Origin field with the name of your own business object Z##_R_TRAVEL. It helps you to distinguish your log entries from entries created by other learners.

Steps

  1. Create a new global class (suggested name: ZCL_##_HANDLER).

    1. In the Project Explorer, right-click the name of your package ZS4D437_## and choose NewABAP Class.

    2. In the Name field, enter ZCL_##_HANDLER and in the Description field, enter Handle Business Event TravelCreated.

    3. Choose Next, assign the new development object to a transport request, then choose Finish.

  2. Enable the global class to handle events of the root entity of your business object Z##_R_TRAVEL.

    1. Add the FOR EVENTS OF statement, followed by the name of your root entity as follows:

      Code Snippet
      1234
      CLASS zcl_##_handler DEFINITION PUBLIC FINAL CREATE PUBLIC FOR EVENTS OF Z##_R_Travel.
  3. Inside the global class, define a local class (suggested name: lcl_handler) that inherits from the global class CL_ABAP_BEHAVIOR_EVENT_HANDLER. Define a private section in the class.

    Hint

    If you use the lcl code template to insert the local class, remember to remove the PUBLIC SECTION and PROTECTED SECTION statements. They are not allowed in sub classes of CL_ABAP_BEHAVIOR_EVENT_HANDLER.
    1. Navigate to the Local Types tab.

    2. Add the following code:

      Code Snippet
      12345678910
      CLASS lcl_handler DEFINITION INHERITING FROM cl_abap_behavior_event_handler. PRIVATE SECTION. ENDCLASS. CLASS lcl_handler IMPLEMENTATION. ENDCLASS.
  4. In the local class, define a private method (suggested name: on_travel_created) to handle the TravelCreated event of the root entity.

    Note

    Remember that you need the IMPORTING addition, even though the event does not yet define a parameter.
    1. Adjust the code as follows:

      Code Snippet
      12345678910111213141516
      CLASS lcl_handler DEFINITION INHERITING FROM cl_abap_behavior_event_handler. PRIVATE SECTION. METHODS on_travel_created FOR ENTITY EVENT IMPORTING new_travels FOR Travel~TravelCreated. ENDCLASS. CLASS lcl_handler IMPLEMENTATION. METHOD on_travel_created. ENDMETHOD. ENDCLASS.
  5. At the beginning of the method implementation, declare an internal table (suggested name: log) with the derived type TABLE FOR CREATE /lrn/437_i_travellog.

    1. Adjust the code as follows:

      Code Snippet
      1234
      METHOD on_travel_created. DATA log TYPE TABLE FOR CREATE /lrn/437_i_travellog. ENDMETHOD.
  6. Implement a loop over the importing parameter and fill the internal table with the key values of the new flight travels. In addition, fill the Origin component with the name of your own business object Z##_R_TRAVEL.

    1. Adjust the code as follows:

      Code Snippet
      12345678910
      METHOD on_travel_created. DATA log TYPE TABLE FOR CREATE /lrn/437_i_travellog. LOOP AT new_travels ASSIGNING FIELD-SYMBOL(<travel>). APPEND VALUE #( AgencyID = <travel>-AgencyId TravelID = <travel>-TravelId Origin = 'Z##_R_TRAVEL' ) TO log. ENDLOOP. ENDMETHOD.
  7. After the loop, implement a MODIFY ENTITIES statement that accesses the /LRN/437_I_TRAVELLOG business object interface.

    1. At the end of the method, insert the following code:

      Code Snippet
      1
      MODIFY ENTITIES OF /LRN/437_I_TravelLog
  8. Create new instances of the root entity TravelLog, passing the fields AgencyID, TravelID, and Origin. As input, use the internal table log.

    Note

    When you use EML to create new instances, always populate the %cid field with non-initial values. An alternative is the use of the AUTO FILL CID addition.
    1. Adjust the code as follows:

      Code Snippet
      12345
      MODIFY ENTITIES OF /LRN/437_I_TravelLog ENTITY TravelLog CREATE AUTO FILL CID FIELDS ( AgencyID TravelID Origin ) WITH log.
  9. Activate the handler class and test the event handling.

    1. Press Ctrl + F3 to activate the development object.

    2. Reopen the preview of your OData UI service and choose Create to create a new flight travel.

    3. Enter all mandatory fields for the flight travel and choose Create.

    4. Take note of the values displayed in the Agency ID field and the Travel ID field.

    5. Open the preview for OData UI service /LRN/UI_437_TRAVELLOG_O2.

    6. Choose Go. (If there are too many entries, enter Z##_R_TRAVEL under Root Entity Name and choose Go again.)

      Result

      You see an entry that corresponds to the flight travel that you have created.