Демоны abap - это фоновые сеансы с неограниченным сроком жизни. Время выполнения которых, зависит только от работы сервера. В случае ошибок, возникших во время работы, демон перезапускается.
Попробуем создать простой демон для примера. Он будет следить за входом пользователя в указанную транзакцию. По большому, счету это повторение демо-примера от SAP. Можно сразу смотреть там.
Для остальных ниже:
1. Создадим класс ZCL_DAEMON_TEST унаследованный от абстрактного класса CL_ABAP_DAEMON_EXT_BASE.
2. В интерфейсы прописываем IF_ABAP_TIMER_HANDLER - для таймера.
2. Переопределим метод ON_ACCEPT - он запускается перед запуском демона и позволяет принять или отклонить запуск. В нашем случае, мы проверяем, что демон запускается именно из нашего класса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
method if_abap_daemon_extension~on_accept. data lv_program_name type program.. try. lv_program_name = cl_oo_classname_service=>get_classpool_name( mc_class_name ). 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. " обработка ошибок, например, запись журнала ошибок! e_setup_mode = co_setup_mode-reject. endtry . endmethod. |
3. Далее переропределяем метод ON_START. Тут мы получаем значение mv_timeout которое будет служить для периода срабатывания таймера.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
method IF_ABAP_DAEMON_EXTENSION~ON_START. TRY. " получение начальных параметров по PCP mv_timeout = i_context->get_start_parameter( )->get_field( 'timeout' ). " инициализация таймера mo_timer = cl_abap_timer_manager=>get_timer_manager( ). CATCH cx_abap_daemon_error cx_ac_message_type_pcp_error cx_abap_timer_error. ENDTRY. endmethod. |
3. Переропределяем метод ON_MESSAGE - срабатывает при получении сообщения. Здесь заполняются атрибуты класса именем искомого пользователя и транзакцией. А также запускаем таймер, по истечении времени которого, сработает событие ON_TIMEOUT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
method if_abap_daemon_extension~on_message. try. mv_uname = i_message->get_field( exporting i_name = mc_user ). mv_tcode = i_message->get_field( exporting i_name = mc_tcode ). try. mo_timer->start_timer( i_timeout = mv_timeout i_timer_handler = me ). catch cx_abap_timer_error. endtry. catch cx_ac_message_type_pcp_error. endtry. endmethod. |
4. Переопределяем метод ON_TIMEOUT . Ищем нужного пользователя и транзакцию среди активных сеансов. Если находим, выводим сообщение для пользователя запустившего демон. Если нет, Перезапускаем таймер.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
method if_abap_timer_handler~on_timeout. data lt_user_tab type table of uinfo. call function 'THUSRINFO' tables usr_tabl = lt_user_tab. if line_exists( lt_user_tab[ bname = mv_uname tcode = mv_tcode ] ). call function 'TH_POPUP' exporting client = sy-mandt user = sy-uname message = conv th_popup( |Пользователь { mv_uname } зашел в транзакцию { mv_tcode }| ). else. try. mo_timer->start_timer( i_timeout = mv_timeout i_timer_handler = me ). catch cx_abap_timer_error. endtry. endif. endmethod. |
5. Далее реализуем три статических метода для Запуска, Передачи сообщения Демону и его Остановки. Думаю, тут особых комментариев не нужно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
method start. " Получим список запущеных ABAP Daemon data(lt_ad_info) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = mc_class_name ). " Можно запустить несколько демонов под одним именем. " В данном случае нам это не надо. check not line_existS( lt_ad_info[ name = iv_daemon_name ] ). " передаем начальные параметры. В нашем случае это интервал для таймера data(lo_pcp) = cl_ac_message_type_pcp=>create( ). lo_pcp->set_field( i_name = 'timeout' i_value = conv #( iv_timeout ) ). " запускаем демон при помощи ABAP Daemon Manager cl_abap_daemon_client_manager=>start( i_class_name = mc_class_name i_name = conv #( iv_daemon_name ) i_priority = cl_abap_daemon_client_manager=>co_session_priority_low i_parameter = lo_pcp ). endmethod. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
method send. " получим список запущеных ABAP Daemon data(lt_run_daemon_list) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = mc_class_name ). " сформируем PCP сообщения и передадим значения имя пользователя и транзакцию data(lo_pcp) = cl_ac_message_type_pcp=>create( ). lo_pcp->set_field( exporting i_name = mc_user i_value = conv #( iv_user ) ). lo_pcp->set_field( exporting i_name = mc_tcode i_value = conv #( iv_tcode ) ). " Отправим сообщение в наш демон read table lt_run_daemon_list assigning field-symbol(<ls_daemon_list>) with key name = iv_daemon_name. if sy-subrc = 0. cl_abap_daemon_client_manager=>attach( <ls_daemon_list>-instance_id )->send( lo_pcp ). endif. endmethod. |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
method stop. data(lt_ad_info) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = mc_class_name ). "У нас по идее только один активный демон, но на всякий случай остановим в цикле. loop at lt_ad_info assigning field-symbol(<ls_info>) WHERE name = iv_daemon_name . cl_abap_daemon_client_manager=>stop( i_instance_id = <ls_info>-instance_id ). endloop. endmethod. |
Также необходимо добавить некоторые атрибуты и константы в класс
1 2 3 4 5 6 7 8 9 |
private section. data MV_TIMEOUT type I . data MO_TIMER type ref to IF_ABAP_TIMER_MANAGER . data MV_UNAME type UNAME . data MV_TCODE type SYST_TCODE . constants MC_TCODE type STRING value 'TCODE' ##NO_TEXT. constants MC_USER type STRING value 'USER' ##NO_TEXT. constants MC_CLASS_NAME type ABAP_DAEMON_CLASS_NAME value 'ZCL_DAEMON_TEST' ##NO_TEXT. |
Готово.
Запускаем демон следующим кодом:
1 2 3 4 |
report z_test_pau. zcl_daemon_test=>start( iv_daemon_name = |test_daemon-{ sy-uname }| iv_timeout = 5000 ). zcl_daemon_test=>send( iv_daemon_name = |test_daemon-{ sy-uname }| iv_user = sy-uname iv_tcode = 'SE91' ). |
Заходим в транзакцию SE91 и получаем сообщение.
Посмотреть запущенные демоны можно в транзакции SMDAEMON.
Останавливаем демон:
1 |
zcl_daemon_test=>stop( iv_daemon_name = |test_daemon-{ sy-uname }| ). |
Немного скриншотов: