Skip to content

ABAP Keyword Documentation →  ABAP − Reference →  Data Interfaces and Communication Interfaces →  ADF - ABAP Daemon Framework →  Examples for ABAP Daemons 

ADF, Creating and Using an ABAP Daemon

This example demonstrates an ABAP Daemon.

Other versions: 7.31 | 7.40 | 7.54

Source Code

    DELETE FROM DATABASE demo_indx_blob(dm)
                ID cl_demo_abap_daemon_broker=>daemon_log.
    COMMIT CONNECTION default.

    get_input( ).

    out = cl_demo_output=>new( ).

    out->next_section( 'ABAP Program' ).

    TRY.

        IF NOT cl_demo_abap_daemon_broker=>check_daemon( ).

          IF start_daemon = abap_true.
            DATA(pcp) = cl_ac_message_type_pcp=>create( ).
            pcp->set_field( i_name = `Version`
                            i_value = `1` ).
            IF cl_demo_abap_daemon_broker=>start_daemon(
                 pcp = pcp ).
              write_log( `Daemon start requested` ).
            ELSE.
              write_log( `Daemon not accepted` ).
            ENDIF.
          ELSE.
            write_log( `Daemon not started` ).
          ENDIF.

        ELSE.

          cl_demo_abap_daemon_broker=>attach_daemon( ).

          CASE abap_true.

            WHEN start_daemon.
              write_log( `Daemon already started` ).

            WHEN get_information.
              write_log( `Information requested` ).
              out->write(
                cl_demo_abap_daemon_broker=>get_daemon_info( ) ).

            WHEN send_message.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `msg` ).
              pcp->set_text( `Hello Daemon!` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `Text message sent` ).

            WHEN trigger_amc.
              DATA(receiver) = NEW amc_receiver( ).
              TRY.
                  cl_amc_channel_manager=>create_message_consumer(
                    i_application_id = 'DEMO_AMC'
                    i_channel_id     = '/demo_text'
                    )->start_message_delivery( i_receiver = receiver ).
                CATCH cx_amc_error INTO DATA(amc_exc).
                  write_log( `Exception, ` && amc_exc->get_text( ) ).
              ENDTRY.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `amc` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `AMC message triggered` ).
              WAIT FOR MESSAGING CHANNELS
                   UNTIL receiver->msg IS NOT INITIAL
                   UP TO 10 SECONDS.
              IF receiver->msg IS NOT INITIAL.
                write_log( |AMC message "{ receiver->msg }" received| ).
              ELSE.
                write_log( `No AMC message received` ).
              ENDIF.

            WHEN trigger_exception.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `err` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `Exception triggered` ).

            WHEN trigger_blocking.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `blk` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `Blocking statement triggered` ).

            WHEN trigger_restart.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `rst` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `Restart triggered` ).

            WHEN trigger_relocation.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `rlo` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `Relocation triggered` ).

            WHEN trigger_stop.
              pcp = cl_ac_message_type_pcp=>create( ).
              pcp->set_field( i_name = `Type`
                              i_value = `stp` ).
              cl_demo_abap_daemon_broker=>send_message( pcp = pcp ).
              write_log( `Stop triggered` ).

            WHEN stop_daemon.
              cl_demo_abap_daemon_broker=>stop_daemon( ).
              write_log( `Daemon stop requested` ).
          ENDCASE.

        ENDIF.
      CATCH cx_abap_daemon_error
            cx_ac_message_type_pcp_error INTO DATA(exc).
        write_log( `Exception, ` && exc->get_text( ) ).
    ENDTRY.

    WAIT UP TO 1 SECONDS.
    out->next_section( 'ABAP Daemon' ).

    DATA(daemon_log) = ``.
    IMPORT daemon_log = daemon_log
           FROM DATABASE demo_indx_blob(dm)
           ID cl_demo_abap_daemon_broker=>daemon_log.
    IF sy-subrc = 0.
      out->write( daemon_log ).
    ENDIF.

    out->display( ).

Description

It implements the most important aspects of an ABAP Daemon and contains the program above and two classes.

Program DEMO_ABAP_DAEMON

This program makes it possible to start an ABAP Daemon interactively and then perform various actions using the daemon. The checkboxes have the following meanings:

  • START_DAEMON
Attempts to start an ABAP Daemon. Only one ABAP Daemon of the ABAP Daemon class in question may exist. When the program starts, a version number is passed in PCP format. If an ABAP Daemon already exists, this daemon is used.
  • GET_INFORMATION
Gets information about the ABAP Daemon and displays it.
  • SEND_MESSAGE
Sends a text message to the ABAP Daemon in PCP format.
  • TRIGGER_AMC
Sends a PCP message to the ABAP Daemon instructing the daemon to send an AMC message. The program waits until the message arrives at a dedicated AMC receiver.
  • TRIGGER_EXCEPTION
Sends a PCP message to the ABAP Daemon, where an exception is raised. The exception produces a short dump that can be viewed in transaction ST22 and the ABAP Daemon is restarted automatically. This raises the version number by 1.
  • TRIGGER_BLOCKING
Sends a PCP message to the ABAP Daemon instructing it to create a statement forbidden in non-blocking mode. This raises an exception and short dump, which can be viewed in transaction ST22. The ABAP Daemon is then restarted automatically. This raises the version number by 1.
  • TRIGGER_RESTART
Sends a PCP message to the ABAP Daemon that restarts the daemon. This raises the version number by 1.
  • TRIGGER_RELOCATION
Sends a PCP message to the ABAP Daemon instructing the daemon to create a new instance of its class on a different AS Instance and delete the previous instance.
  • TRIGGER_STOP
Sends a PCP message to the ABAP Daemon instructing it to stop.
  • STOP_DAEMON
Stops the daemon.

The output of the program displays the individual actions in both the program and in the ABAP Daemon. To do this, the daemon writes entries to the export/import table DEMO_INDX_BLOB, which are then read by the ABAP program after a short delay. If the system in question is slow, this delay may not be long enough to display all actions in the daemon. The ABAP Daemon on the current AS Instance can be viewed in parallel to the execution of the program using the transaction SMDAEMON.

Class CL_ABAP_DAEMON_BROKER

The program DEMO_ABAP_DAEMON does not work directly with the class CL_ABAP_DAEMON_CLIENT_MANAGER and any reads performed on this class are wrapped in the class CL_DEMO_ABAP_DAEMON_BROKER. This is because CL_ABAP_DAEMON_CLIENT_MANAGER can only be used to access an ABAP Daemon in the program where the daemon was started. It must be possible for the daemon itself to create an instance of its class in this example, which means the reads must be moved to a program that can be accessed from both the ABAP program and the ABAP Daemon. To enable this, CL_DEMO_ABAP_DAEMON_BROKER contains the following methods:

  • CHECK_DAEMON
METHOD check_daemon.
  DATA(daemon_info) =
    cl_abap_daemon_client_manager=>get_daemon_info(
      i_class_name = daemon_class ).

  instance_id =
     VALUE #( daemon_info[ name = daemon_name ]-instance_id
              OPTIONAL ).

  success = COND #( WHEN instance_id IS NOT INITIAL THEN abap_true ).
ENDMETHOD.
This method determines whether a daemon of the ABAP Daemon class CL_DEMO_ABAP_DAEMON already exists and saves its ID for further use. The program DEMO_ABAP_DAEMON starts a daemon of the class CL_DEMO_ABAP_DAEMON only if no daemon exists. The method shown here for creating an ABAP Daemon as a singleton is not, however, 100% reliable. It is possible that further daemons are created in the same class in the time between starting the daemon and its being returned by the method GET_DAEMON_INFO. A fully reliable program, however, would be too detailed for this simple example.
  • START_DAEMON
METHOD start_daemon.
  DATA stack TYPE abap_callstack.
  CALL FUNCTION 'SYSTEM_CALLSTACK'
    IMPORTING
      callstack = stack.
  IF VALUE #( stack[ 2 ]-mainprogram OPTIONAL )
     <> 'DEMO_ABAP_DAEMON' AND
     VALUE #( stack[ 2 ]-mainprogram OPTIONAL )
     <> 'CL_DEMO_ABAP_DAEMON===========CP'.
    RETURN.
  ENDIF.

  cl_abap_daemon_client_manager=>start(
    EXPORTING
      i_class_name  = daemon_class
      i_name        = daemon_name
      i_parameter   = pcp
      i_destination = destination
    IMPORTING
      e_instance_id = instance_id
      e_setup_mode  = DATA(setup_mode) ).

  success =
    COND #( WHEN setup_mode =
                   if_abap_daemon_extension=>co_setup_mode-accept
            THEN abap_true ).
ENDMETHOD.
This method wraps the method START of the class CL_ABAP_DAEMON_CLIENT_MANAGER and starts an ABAP Daemon of the ABAP Daemon class CL_DEMO_ABAP_DAEMON. Any callers are checked to ensure that the method is only used in the program DEMO_ABAP_DAEMON 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.
  • GET_DAEMON_INFO
METHOD get_daemon_info.
  daemon_info =
    cl_abap_daemon_client_manager=>get_daemon_info(
      i_class_name = daemon_class ).
ENDMETHOD.
This method wraps the identically named method of the class CL_ABAP_DAEMON_CLIENT_MANAGER and returns information about the ABAP Daemons of the ABAP Daemon class CL_DEMO_ABAP_DAEMON.
  • ATTACH_DAEMON
METHOD attach_daemon.
  daemon_handle = cl_abap_daemon_client_manager=>attach(
    i_instance_id = instance_id ).
ENDMETHOD.
This method wraps the method ATTACH of the class CL_ABAP_DAEMON_CLIENT_MANAGER. The returned reference to the ABAP Daemon handle is saved in the private attribute DAEMON_HANDLE.
  • SEND_MESSAGE
METHOD send_message.
  IF daemon_handle IS INITIAL.
    RAISE EXCEPTION TYPE cx_abap_daemon_error
      EXPORTING
        textid = cx_abap_daemon_error=>action_not_permitted.
  ENDIF.
  daemon_handle->send( pcp ).
ENDMETHOD.
This method is used to send PCP messages to the ABAP Daemon. To do this, the method SEND of the ABAP Daemon handle is used, which is referenced in the private attribute DAEMON_HANDLE.
  • STOP_DAEMON
METHOD stop_daemon.
  cl_abap_daemon_client_manager=>stop(
    i_instance_id = instance_id ).
ENDMETHOD.
This method wraps the method STOP of the class CL_ABAP_DAEMON_CLIENT_MANAGER and is used to stop the ABAP Daemon.

The name of the ABAP Daemon and the ABAP Daemon class are defined as constants of the class CL_ABAP_DAEMON_BROKER.

Class CL_ABAP_DAEMON

The class CL_DEMO_ABAP_DAEMON is a subclass of CL_ABAP_DAEMON_EXT_BASE and is the ABAP Daemon class for this example. It implements the most important methods of the interface IF_ABAP_DAEMON_EXTENSION and further standalone helper methods. It also implements the interface IF_ABAP_TIMER_HANDLER so that it can be an ABAP Timer handler for an ABAP Timer. Although daemons should never be stopped in practice, daemons created by this example are deleted automatically after an hour (if not stopped explicitly first).

  • IF_ABAP_DAEMON_EXTENSION~ON_ACCEPT
METHOD if_abap_daemon_extension~on_accept.
  TRY.
      DATA(caller) = i_context_base->get_start_caller_info( )-program.
      IF caller = 'CL_DEMO_ABAP_DAEMON_BROKER====CP'.
        e_setup_mode = co_setup_mode-accept.
      ENDIF.
    CATCH cx_abap_daemon_error.
      RETURN.
  ENDTRY.
ENDMETHOD.
Before the daemon is started, this method checks whether the calling program is the class pool of the class CL_DEMO_ABAP_DAEMON_BROKER. Only in this case is the output parameter E_SETUP_MODE set so that the daemon can be started.
  • IF_ABAP_DAEMON_EXTENSION~ON_START
METHOD if_abap_daemon_extension~on_start.
  TRY.
      set_context( context = i_context
                   version = i_context->get_start_parameter(
                                          )->get_field(
                                             i_name = `Version` ) ).
      write_log( |Daemon started as version {
                    i_context->get_application_parameter(
                       )->get_field( i_name = `Version` )
                  } on { sy-host }| ).

    CATCH cx_ac_message_type_pcp_error
          cx_abap_daemon_error
          cx_abap_timer_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
Directly after the daemon is started, this method calls the helper method SET_CONTEXT, which saves context information and starts a timer. One example of context information here is the version number passed by the caller when the daemon is started.
  • IF_ABAP_DAEMON_EXTENSION~ON_MESSAGE
METHOD if_abap_daemon_extension~on_message.
  TRY.
      DATA(type) = i_message->get_field( i_name = `Type` ).
      CASE type.
        WHEN `msg`.
          write_log(
            |Message "{ i_message->get_text( )
              }" received on { sy-host }| ).
        WHEN `amc`.
          write_log( `Sending AMC message in daemon` ).
          send_amc( `Hello from daemon` ).
        WHEN `err`.
          write_log( `Raising exception in daemon` ).
          MESSAGE `Type X message in daemon` TYPE 'X'.
        WHEN `blk`.
          write_log( `Executing blocking statement in daemon` ).
          WAIT UP TO 1 SECONDS.
        WHEN `rst`.
          write_log( `Restarting from daemon` ).
          i_context->restart( ).
        WHEN `rlo`.
          write_log( `Relocating daemon` ).
          relocate( ).
        WHEN `stp`.
          write_log( `Stopping from daemon` ).
          i_context->stop( ).
      ENDCASE.
    CATCH cx_ac_message_type_pcp_error
          cx_abap_daemon_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
This method evaluates the inbound PCP messages and performs the actions in question directly or calls helper methods from the ABAP Daemon class.
  • IF_ABAP_DAEMON_EXTENSION~ON_ERROR
METHOD if_abap_daemon_extension~on_error.
  set_context( i_context ).
  TRY.
      write_log(
        `Daemon restarted after error and version increased to ` &&
         i_context->get_application_parameter(
                      )->get_field( i_name = `Version` ) ).
    CATCH cx_ac_message_type_pcp_error
          cx_abap_daemon_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
IF_ABAP_DAEMON_EXTENSION~ON_RESTART
METHOD if_abap_daemon_extension~on_restart.
  set_context( i_context ).
  TRY.
      write_log(
        `Daemon restarted and version increased to ` &&
         i_context->get_application_parameter(
                      )->get_field( i_name = `Version` ) ).
    CATCH cx_ac_message_type_pcp_error
          cx_abap_daemon_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
These methods call the helper method SET_CONTEXT to set the context information again after a restart.
  • IF_ABAP_DAEMON_EXTENSION~ON_SERVER_SHUTDOWN
METHOD if_abap_daemon_extension~on_server_shutdown.
  relocate( ).
ENDMETHOD.
When the current AS Instance is shut down, this method calls the helper method RELOCATE to move the daemon to a different AS Instance.
  • IF_ABAP_TIMER_HANDLER~ON_TIMEOUT
METHOD if_abap_timer_handler~on_timeout.
  TRY.
      write_log( `Timeout reached, stopping daemon` ).
      daemon_context->stop( ).
    CATCH cx_abap_daemon_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
In the case of a timeout event of the ABAP Timer set in SET_CONTEXT, this method stops the daemon.
  • SET_CONTEXT
METHOD set_context.
  daemon_context = context.
  TRY.
      DATA(pcp) = cl_ac_message_type_pcp=>create( ).
      pcp->set_field(
        i_name = `Version`
        i_value = COND #(
                    WHEN version IS NOT SUPPLIED
                      THEN context->get_application_parameter(
                             )->get_field( i_name = `Version` ) + 1
                      ELSE version ) ).
      context->set_application_parameter( i_parameter = pcp ).

      cl_abap_timer_manager=>get_timer_manager(
        )->start_timer(
          i_timer_handler = me
          i_timeout = 3600 * 1000 ).
      write_log( `Daemon timeout set to one hour` ).

    CATCH cx_ac_message_type_pcp_error
          cx_abap_daemon_error
          cx_abap_timer_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
This method sets an attribute of the class to the context object and uses its method SET_APPLICATION_PARAMETER to save the current version number in the ABAP Daemon memory in PCP format. If SET_CONTEXT is called after a restart, the previous version number is fetched from the ABAP Daemon memory and raised by 1. Furthermore, SET_CONTEXT initializes an ABAP Timer responded to by the method IF_ABAP_TIMER_HANDLER~ON_TIMEOUT of the current daemon.
  • SEND_AMC
METHOD send_amc.
  TRY.
      CAST if_amc_message_producer_text(
             cl_amc_channel_manager=>create_message_producer(
               i_application_id = 'DEMO_AMC'
               i_channel_id     = '/demo_text'
               i_suppress_echo  = 'X' )
        )->send( i_message = msg ).
    CATCH cx_amc_error INTO DATA(amc_exc).
      write_log( `Exception, ` && amc_exc->get_text( ) ).
  ENDTRY.
ENDMETHOD.
This method sends an AMC message.
  • RELOCATE
METHOD relocate.
  DATA list TYPE TABLE OF msxxlist WITH EMPTY KEY.
  CALL FUNCTION 'TH_SERVER_LIST'
    TABLES
      list   = list
    EXCEPTIONS
      OTHERS = 4.
  IF sy-subrc <> 0 OR lines( list ) < 2.
    write_log( `No other application server available`).
    RETURN.
  ENDIF.

  DELETE list WHERE host = sy-host.
  DATA(server) = list[
    cl_abap_random_int=>create(
      seed = CONV #( sy-uzeit )
      min  = 1
      max  = lines( list ) )->get_next( ) ].

  TRY.
      IF cl_demo_abap_daemon_broker=>start_daemon(
            pcp = daemon_context->get_application_parameter( )
            destination = server-name ).
        write_log( `Daemon relocated to ` && server-host && ` `  ).
        daemon_context->stop( ).
      ELSE.
        write_log( `Daemon not accepted on ` && server-host ).
      ENDIF.
    CATCH cx_abap_daemon_error INTO DATA(exc).
      write_log( `Exception, ` && exc->get_text( ) ).
  ENDTRY.

ENDMETHOD.
This method attempts to move the current daemon to a different AS Instance. To do this, an AS Instances is selected at random from a list in the current AS ABAP. This application server is then used as a destination for starting a daemon of the current ABAP Daemon class using CL_ABAP_DAEMON_BROKER. Here, the current context information from the ABAP Daemon memory is passed directly as start parameters in PCP format. The current daemon is then stopped.
  • WRITE_LOG
METHOD write_log.
  DATA ts TYPE timestampl.
  GET TIME STAMP FIELD ts.

  DATA(daemon_log) = ``.
  IMPORT daemon_log = daemon_log
         FROM DATABASE demo_indx_blob(dm)
         ID cl_demo_abap_daemon_broker=>daemon_log.
  daemon_log = daemon_log && |{ ts TIMESTAMP = ISO }: { msg }\n|.
  EXPORT daemon_log = daemon_log
         TO DATABASE demo_indx_blob(dm)
         ID cl_demo_abap_daemon_broker=>daemon_log.
  COMMIT CONNECTION default.
ENDMETHOD.
This method writes log entries to the export/import table DEMO_INDX_BLOB as a string. It is called by the other methods to log the actions of the daemon for the output of the program DEMO_ABAP_DAEMON.

In this example, the remaining methods of the interface IF_ABAP_DAEMON_EXTENSION only write log entries.


Note

This simple example does not guarantee that an ABAP Daemon in the ABAP Daemon class CL_ABAP_DAEMON is a system-wide singleton. Any parallel reads that cause restarts can be the source of multiple unwanted daemons. This applies in particular when moving daemons to other AS Instances. A great deal more work is required to create a real singleton. See the class CL_AD_EXT_SIMPLE_DAEMON, which can be used by the program RS_ABAP_DAEMON_TEST.