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
- GET_INFORMATION
- SEND_MESSAGE
- TRIGGER_AMC
- TRIGGER_EXCEPTION
- TRIGGER_BLOCKING
- TRIGGER_RESTART
- TRIGGER_RELOCATION
- TRIGGER_STOP
- STOP_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
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.
- 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.
- GET_DAEMON_INFO
daemon_info =
cl_abap_daemon_client_manager=>get_daemon_info(
i_class_name = daemon_class ).
ENDMETHOD.
- ATTACH_DAEMON
METHOD attach_daemon.
daemon_handle = cl_abap_daemon_client_manager=>attach(
i_instance_id = instance_id ).
ENDMETHOD.
- 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.
- STOP_DAEMON
METHOD stop_daemon.
cl_abap_daemon_client_manager=>stop(
i_instance_id = instance_id ).
ENDMETHOD.
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
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.
- 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.
- 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.
- 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.
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.
- IF_ABAP_DAEMON_EXTENSION~ON_SERVER_SHUTDOWN
METHOD if_abap_daemon_extension~on_server_shutdown.
relocate( ).
ENDMETHOD.
- 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.
- 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.
- 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.
- 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.
- 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.
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.