ABAP Daemon Framework

Use ABAP Daemons, to implement event handling.  

ABAP Daemons are long-living ABAP sessions. The application server takes care that once an ABAP Daemon was started, there is no limit for the lifetime of such ABAP session. 

Even if there is an ABAP runtime error, or a work process in the application server crashes, the ABAP Daemon session gets restarted immediately.

ABAP Daemons have event-driven programming model (similar to ABAP Channels) which contains the typical event handler API for your implementation needs with such methods like on_start, on_message, on_error etc.

You can start an ABAP Daemon and send messages to it. Please consider, that each ABAP Daemon runs under a given user, and therefore the messages to it can be sent only within the same client. If you need to send the system-wide messages to your ABAP Daemon you can use ABAP Messaging Channel (AMC).

ABAP Daemons are available with SAP NetWeaver AS ABAP 7.52. The startup configuration was added to ABAP Daemons in ABAP platform 1809.

Demo programs

  • DEMO_ABAP_MINI_DAEMON
  • demo_abap_daemon

Use cases

  1. Just imagine if multiple sensors are connected to the ABAP system, the ABAP server receives too many incoming messages. These messages cannot be processed concurrently, but it is possible to queue them by starting an ABAP Daemon session and sending messages to it. This central scheduling ABAP Daemon session will be always available for event processing.
  2. You can also use ABAP Daemons for lightweight background activities like e.g. tracing, logging, clean-up of resources, synchronization of caches. The advantage is, that the logging, tracing etc. messages are sent to the ABAP Daemon without DB commit using ABAP Messaging Channel (AMC), therefore sending event to the ABAP Daemon will not break your LUW (Logical Unit of Work).
  3. You can also use ABAP Daemons for very frequent periodic tasks e.g. like health checks. The advantage is that you do not need to create a new ABAP session for each task execution and no background work process is needed. For heavyweight periodic tasks, that run at most once a day, we recommend using batch jobs.
  4. You can use ABAP Daemon also as a proxy session for external communication
  5. Let suppose user/business want to procure some asset/item ( e.g. laptop/ Heavy machinery for factory). So in this process, different document are created to complete the procurement. After each documents(or events) creation/posting, I want certain custom table to be updated & outbound IDOC message should be trigger. The step of updating the table & sending IDOC  will be handle by daemon instance. Below are I tried to explain how complete business process will flow for my example.
  6. Example from SCN 
    • ( https://blogs.sap.com/2020/05/17/implement-adf-abap-daemon-framework/ )
    • Business/user raise request in some portal of the legacy system for procurement of asset and request details sent to S/4 HANA (1909) system via interfacing technology AIF (Application Interface Framework).
    • Upon receiving AIF message, different documents created (e.g. Internal Order, Asset Master & Purchase Requisition). 
    • The document created in this step needs to be updated in the custom table (for reference) & trigger an outbound is sent.
    • The next step, Purchase Order created with reference to the Purchase requisition. The Purchase order also needs to be updated in the custom table  & trigger an outbound is sent.
    • Next step, supplier sends physical asset. This leads good receipt in the S/4 HANA. The goods receipt details were received via AIF which results in Material document posting. The Material documents need to be updated in the custom table  & trigger an outbound is sent.
    • Lastly, the Supplier sends an invoice via AIF which leads to purchase Invoice creation in the system in S/4 HANA. The Invoice documents need to be updated in the custom table  & an outbound is sent
    • Each Inbound/Outbound message or document create/change/delete can be considered as event in ABAP.

Implementation

Example: https://developers.sap.com/tutorials/abap-connectivity-daemon-simple.html

Create a new ABAP Daemon class

create a new ABAP class ZCL_TUTORIAL_SIMPLE_DAEMON and set CL_ABAP_DAEMON_EXT_BASE as its superclass.e e.g. messaging proxy in MQTT scenarios

Implement ON_ACCEPT method

its ON_ACCEPT method is called to determine if the daemon should start.

METHOD if_abap_daemon_extension~on_accept.
  TRY.
      DATA lv_program_name TYPE program.
      lv_program_name = cl_oo_classname_service=>get_classpool_name( 'ZCL_TUTORIAL_SIMPLE_DAEMON' ).

      IF i_context_base->get_start_caller_info( )-program = lv_program_name.
        e_setup_mode = co_setup_mode-accept.
      ELSE.
        e_setup_mode = co_setup_mode-reject.
      ENDIF.
    CATCH cx_abap_daemon_error.
      " to do: error handling, e.g. write error log!
      e_setup_mode = co_setup_mode-reject.
  ENDTRY.
ENDMETHOD.

Implement ON_START method

In this simple scenario, you will pass a parameter timeout via PCP. The daemon will then set up a timer to display a popup message each time the timeout was reached. A counter will be used to stop the timer after five messages.


At first, you need to create some private member variables to store the necessary data. Add these lines to the PRIVATE SECTION of your class:

DATA: mv_timeout TYPE i,
      mo_timer   TYPE REF TO if_abap_timer_manager,
      mv_counter TYPE i.

In the ON_START method you can now retrieve the timeout value from PCP and initiate the timer afterwards. To do so, copy the following implementation:
METHOD if_abap_daemon_extension~on_start.
  TRY.
      " retrieve timeout from PCP start parameters
      mv_timeout = i_context->get_start_parameter( )->get_field( 'timeout' ).

      " start timer for displaying messages
      mo_timer = cl_abap_timer_manager=>get_timer_manager( ).
      mo_timer->start_timer( i_timeout = mv_timeout i_timer_handler = me ).

    CATCH cx_abap_daemon_error cx_ac_message_type_pcp_error cx_abap_timer_error.
      " to do: error handling, e.g. write error log!
  ENDTRY.
ENDMETHOD.

Implement ON_TIMEOUT method

While your ABAP Daemon is running in the background, the ON_TIMEOUT method will be triggered when the timeout elapses. Therefore, you need to implement a handler interface. Add this code to the PUBLIC SECTION of your class:
INTERFACES if_abap_timer_handler.

Now insert the following ON_TIMEOUT method into your class implementation. It will display a popup message using function module TH_POPUP and restart the timer five times:

METHOD if_abap_timer_handler~on_timeout.
  " increment the loop counter
  ADD 1 TO mv_counter.

  " display popup message
  CALL FUNCTION 'TH_POPUP'
    EXPORTING
      client  = sy-mandt
      user    = sy-uname
      message = CONV th_popup( |Timeout triggered. Number of loops: { mv_counter }| ).

  " restart the timer if any loops are remaining
  IF mv_counter < 5.
    TRY.
        mo_timer->start_timer( i_timeout = mv_timeout i_timer_handler = me ).
      CATCH cx_abap_timer_error.
        " to do: error handling, e.g. write error log!
    ENDTRY.
  ENDIF.
ENDMETHOD.

Implement ON_MESSAGE method

You can also send PCP messages to your daemon while it is continuously running in the background. The ON_MESSAGE method will be called every time the daemon receives a PCP message.

In this tutorial, you will send a plain text to the daemon, which then will be shown in a popup message. As a real life example, you could imagine the ABAP Daemon receiving live data from a sensor and sending a notification under certain conditions.

Replace your ON_MESSAGE method with this code:

METHOD if_abap_daemon_extension~on_message.
  TRY.
      " get text from PCP message
      DATA(lv_text) = i_message->get_text( ).

      " display popup
      CALL FUNCTION 'TH_POPUP'
        EXPORTING
          client  = sy-mandt
          user    = sy-uname
          message = CONV th_popup( |Message received: { lv_text }| ).
    CATCH cx_ac_message_type_pcp_error.
      " to do: error handling, e.g. write error log!
  ENDTRY.
ENDMETHOD.

Implement the static method START

This simple ABAP Daemon will be started by a static method. Thus, you need to create a new method START in your daemon class with two import parameters:

IV_DAEMON_NAME: The name of the daemon
IV_TIMEOUT: The timeout between two successive timer events (milliseconds)
To do so, paste the following code into the PUBLIC SECTION of the class definition:


CLASS-METHODS start
  IMPORTING
    iv_daemon_name TYPE string
    iv_timeout     TYPE i
  RAISING
    cx_abap_daemon_error
    cx_ac_message_type_pcp_error.

instantiate the ABAP Daemon using the ABAP Daemon Manager. Its static method CL_ABAP_DAEMON_CLIENT_MANAGER=>START requires the name and priority of the daemon as parameters. Additionally, you can pass the startup parameters as a PCP message.

Copy these lines into your class implementation:
METHOD start.
  " set ABAP Daemon start parameters
  DATA(lo_pcp) = cl_ac_message_type_pcp=>create( ).
  lo_pcp->set_field( i_name = 'timeout' i_value = CONV #( iv_timeout ) ).

  " start the daemon application using the ABAP Daemon Manager
  cl_abap_daemon_client_manager=>start(
      i_class_name = 'ZCL_TUTORIAL_SIMPLE_DAEMON'
      i_name       = CONV #( iv_daemon_name )
      i_priority   = cl_abap_daemon_client_manager=>co_session_priority_low
      i_parameter  = lo_pcp ).
ENDMETHOD.

Implement the static method SEND

Implement a static method SEND which will be used to send text to the daemon and therefore trigger its ON_MESSAGE method. The method will need the following importing parameters:

IV_DAEMON_NAME: The name of the daemon that will receive the message.
IV_TEXT: The text message that will be send to the daemon.

LASS-METHODS send
  IMPORTING
    iv_daemon_name TYPE string
    iv_text        TYPE string
  RAISING
    cx_abap_daemon_error
    cx_ac_message_type_pcp_error.

In order to send PCP messages to your daemon, you first have to retrieve a list of all running ABAP Daemon instances of your class ZCL_TUTORIAL_SIMPLE_DAEMON. Afterwards, you can compare the name of each instance to IV_DAEMON_NAME and send the PCP message accordingly.

Insert the following code into the class implementation:

METHOD send.
  " retrieve the list of ABAP Daemon instances
  DATA(lt_ad_info) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = 'ZCL_TUTORIAL_SIMPLE_DAEMON').

  " create PCP message with text
  DATA(lo_pcp) = cl_ac_message_type_pcp=>create( ).
  lo_pcp->set_text( iv_text ).

  " for each running daemon instance of this class
  LOOP AT lt_ad_info ASSIGNING FIELD-SYMBOL(<ls_info>).

    " send a message if the names match
    IF iv_daemon_name = <ls_info>-name.
      cl_abap_daemon_client_manager=>attach( <ls_info>-instance_id )->send( lo_pcp ).
    ENDIF.

  ENDLOOP.
ENDMETHOD.

Run the ABAP Daemon

zcl_tutorial_simple_daemon=>start( iv_daemon_name = 'simple_daemon' iv_timeout = 10000 ).

Send messages to the ABAP Daemon
DATA(lv_text) = `This is a simple ABAP Daemon message sent via PCP.`.
zcl_tutorial_simple_daemon=>send( iv_daemon_name = 'simple_daemon' iv_text = lv_text ).

Stop the ABAP Daemon

CLASS-METHODS stop
  IMPORTING
    iv_daemon_name TYPE string
  RAISING
    cx_abap_daemon_error.

METHOD stop.
  " retrieve the list of ABAP Daemon instances
  DATA(lt_ad_info) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = 'ZCL_TUTORIAL_SIMPLE_DAEMON').

  " for each running daemon instance of this class
  LOOP AT lt_ad_info ASSIGNING FIELD-SYMBOL(<ls_info>).

    " stop the daemon if the names match
    IF iv_daemon_name = <ls_info>-name.
        cl_abap_daemon_client_manager=>stop( i_instance_id = <ls_info>-instance_id ).
    ENDIF.

  ENDLOOP.
ENDMETHOD.

zcl_tutorial_simple_daemon=>stop( iv_daemon_name = 'simple_daemon' ).

Comments