Wednesday, 22 September 2021

Create a simple Daemon in ABAP

What is a Daemon in ABAP Language?

A daemon is a utility program that runs continuously by itself and without the control of an interactive user on a multi-tasking operating system in the background to monitor and take care of certain subsystems or show immediate reaction to events. Daemons perform explicit actions at predefined times or in response to certain events.

ABAP daemons abilities which provided by the ABAP Daemon Framework (ADF), are more alike to its counterparts in linux or windows. They are being used to handle events in a reliable way by running in sessions with unlimited lifetime. ABAP programs can communicate with the daemons by sending messages to them using ABAP Messaging Channels and message type Push Channel Protocol (PCP) which is a communications format like a simple HTTP message.

Daemons are robust against errors, i.e., a daemon session is not terminated after running into an error (E/A/X messages). However, An ABAP daemon is created again automatically every time a runtime error or a message of type E, A, or X causes a program termination. When its AS instance is shut down, a daemon can be moved to another AS instance by creating a new daemon containing the same context information as the preceding daemon. This enables the new daemon to do the same job.

ABAP Daemon Framework (ADF)

SAP Netweaver 7.52 present ABAP Daemon Framework or ADF in a short form as a new feature. The ADF is an API that allows you to create and manage daemons in ABAP. An ABAP daemon is an instance of an ABAP daemon class that lives in a special ABAP daemon session of the application server. ABAP programs themselves use the ABAP daemon manager to access ABAP daemons.

As said, the processing of an ABAP daemon takes place in the background and controlled using events. A user of a daemon or the ABAP runtime framework can raise ABAP daemon events to which the daemon reacts using predefined interface methods. A daemon must always be ready to react to inbound events and this is ensured by running ABAP daemon processing in a non-blocking mode to prevent the runtime error DAEMON_ILLEGAL_STATEMENT in ABAP daemon processing and a subsequent restart of the daemon.

ABAP Daemon Classes

An ABAP daemon class is a global class that inherits from the abstract system class CL_ABAP_DAEMON_EXT_BASE. ABAP daemon class inherits the methods of the interface IF_ABAP_DAEMON_EXTENSION that it uses to react to ABAP daemon events if they are implemented in the ABAP daemon class. A detailed explanation on these methods can be found in the related part of the documentation.

An ABAP daemon class can contain further helper methods and can call any number of other procedures in its methods. The interface IF_ABAP_TIMER_HANDLER can also be implemented to turn an ABAP daemon class into an ABAP timer handler and hence to react to ABAP timer events. For example, to wait for certain events or to stop the daemon after a certain time. An ABAP daemon session is created by ABAP Daemon Framework when an ABAP daemon is instantiated from an ABAP daemon class.

Create a Simple ABAP Daemon

Let us now create ABAP daemons and interact with them using PCP messages. As said, ABAP daemons are instances of an ABAP daemon class that extend the base class CL_ABAP_DAEMON_EXT_BASE. 

Step 1: Create a new ABAP Daemon class

As a first step, create an ABAP class ZCL_ABAP_SIMPLE_DAEMON and set CL_ABAP_DAEMON_EXT_BASE as a super class. From this super class, our daemon will inherit different methods from the IF_ABAP_DAEMON_EXTENSION interface that will allow us to respond to different events. Now you see a list of the 9 inherited methods which must be redefined.

Daemon in ABAP, SAP ABAP Tutorial and Material, SAP ABAP Exam Prep, SAP ABAP Study Materials, SAP ABAP Career, SAP ABAP Preparation

Figure : List of inherited methods of IF_ABAP_DAEMON_EXTENSION interface which must be redefined.

Step 2: Implement ON_ACCEPT method

Before a new instance of the ABAP daemon class can be created, its ON_ACCEPT method is called to determine if the daemon should start. It accepts all start requests from your own class and rejects all requests from any other program. This is achieved by checking the calling program that initiated the 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_ABAP_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: for productive applications, error handling, e.g. write error log!
      e_setup_mode = co_setup_mode-reject.
  ENDTRY.
ENDMETHOD.

Step 3: Implement ON_START method

To start the ABAP daemon, the ON_START method is executed. You can include a PCP message containing arbitrary start-up parameters which can be accessed in the 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:

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

You also need to add IF_ABAP_TIMER_HANDLER as an interface of the daemon class. 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: for productive applications, error handling, e.g. write error log!
  ENDTRY.
ENDMETHOD.

Step 4: 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, if_abap_timer_handler, to the PUBLIC SECTION of your class. By implementing the following ON_TIMEOUT method into your class, a popup message using function module TH_POPUP will be displayed, and the timer will be restarted 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: for productive applications, error handling, e.g. write 
error log!
    ENDTRY.
  ENDIF.
ENDMETHOD.

You can also send PCP messages to your daemon while it is continuously running in the background.

Step 5: Implement ON_MESSAGE method

ON_MESSAGE is a useful method from which the daemon receives user input and responds. The ON_MESSAGE method will be called every time the daemon receives a PCP message. As the daemon receives user input, a pop-up dialog box will be popped up, which is displayed in SAPGUI.

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: for productive applications, error handling, e.g. write error log!
  ENDTRY.
ENDMETHOD.

You must consider the fact that only the program which runs the daemon can then use this daemon via the manager daemon. The idea is to have the daemon created via the daemon class itself. Therefore, you need to add 3 other methods to the class: START, SEND and STOP to manage the daemon.

Step 6: 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. This method wraps the method START of the class CL_ABAP_DAEMON_CLIENT_MANAGER and starts an ABAP daemon of the ABAP daemon class.  Its static method CL_ABAP_DAEMON_CLIENT_MANAGER=>START requires the name and priority of the daemon as parameters. Additionally, you can pass the start-up parameters as a PCP message.

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_ABAP_SIMPLE_DAEMON'
      i_name       = CONV #( iv_daemon_name )
      i_priority   = cl_abap_daemon_client_manager=>co_session_priority_low
      i_parameter  = lo_pcp ).
ENDMETHOD.

Any callers are checked to ensure that the method is only used in the related program and in the ABAP daemon class. The ID of the started daemon is saved for further use. If the ABAP daemon class is accepted when started, the return value is not initial.

Step 7: Implement the static method SEND and run the daemon

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.

Paste the code below into the PUBLIC SECTION of the class definition.

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

To send PCP messages to your daemon, you need to retrieve a list of all running ABAP daemon instances of your class ZCL_ABAP_SIMPLE_DAEMON. Afterwards, you can compare the name of each instance to IV_DAEMON_NAME and send the PCP message accordingly.

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_ABAP_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.

You can now launch your daemon by simply calling the static START method created previously. Therefore, create a new ABAP Program Z_ABAP_SIMPLE_DAEMON_START with only one line of code:

REPORT Z_ABAP_SIMPLE_DAEMON_START.
zcl_ABAP_simple_daemon=>start( iv_daemon_name = 'simple_daemon' iv_timeout = 10000 ).

You should see a popup notification every 10 seconds:

Step 8: Stop the ABAP Daemon

You can monitor all running ABAP daemons using transaction SMDAEMON in SAPGUI. There you can see their state, check for errors even restart, and terminate them.

To stop your daemon, select it from the list and go to ABAP Daemon > Terminate Daemon.

Alternatively, you can also create a static STOP method. Therefore, add this to the PUBLIC SECTION of your class definition:

  CLASS-METHODS stop
  IMPORTING
    iv_daemon_name TYPE string
  RAISING
    cx_abap_daemon_error.

And implement

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_ABAP_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.

This method wraps the method STOP of the class CL_ABAP_DAEMON_CLIENT_MANAGER and is used to stop the ABAP Daemon. Now you can stop your daemon by executing

zcl_ABAP_simple_daemon=>stop( iv_daemon_name = 'simple_daemon' ).
from any ABAP Program. 

Daemons Use Cases


Daemons main use cases are handling events or managing simple background activities as well as frequent periodic tasks like tracing, logging, clean-up of resources etc. , without harming your LUW (Logical Unit of Work).

No comments:

Post a Comment