Речь пойдет об прослушивании одного процесса другим. Данная функциональность в ABAP не представлена т.к. в языке нет потоков в классическом понимании этого слова. Однако, комбинируя некоторыми дополнительными возможностями, можно добиться эффекта listener’а.
Listener представляет из себя отдельный поток, который наблюдает за чем-то, например пока внутреннее состояние объекта не изменилось, запись в базу данный или пользователь не нажал на кнопку и т.д. Как только произошло действие за которым мы наблюдаем, к нам в основную программу (основной поток) возвращается информация об этом.
Принцип реализации
Есть в ABAP такая штука, как ABAP Channels. Позволяет делать соединение между двумя процессами.
Для настройки есть транзакция SAMC.
В качестве примера я сделаю приложение — чат. Для начала мне нужно создать программу Z92_CHAT и группу функций Z92TASK_CONNECTOR.
После этого можно открывать транзакцию по настройки каналов SAMC.
Как только откроете транзакцию слева будет дерево объектов, нажимаем правой кнопкой мыши и создаем свой канал. Я назову свой Z_CHANNEL_TEST. В блоке channels создам канал teleport.
Message Type ID будет text, Activity Scope — Client.
Далее необходимо добавить программы с которыми мы будем работать. В качестве отправителя сообщений у меня будет Z92_CHAT, в качестве получателя группа функций Z92TASK_CONNECTOR.
В конечном итоге получается следующая настройка

Открываем программу Z92_CHAT, пишем следующий код.
1 2 3 4 5 |
data: lo_producer_text type ref to if_amc_message_producer_text. lo_producer_text ?= cl_amc_channel_manager=>create_message_producer( i_application_id = 'Z_CHANNEL_TEST' i_channel_id = '/teleport' ). lo_producer_text->send( i_message = 'Hello from Z92_CHAT' ). |
Открываем группу функций Z92TASK_CONNECTOR, создаем функциональный модуль Z92_CONNECTOR (у ФМ должен быть выходной параметр типа string, понадобится в будущем).
В ФМ нужно создать класс для имплементации интерфейса if_amc_message_receiver_text, он содержит только один метод RECEIVE. А так же добавим глобальную переменную message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Data: message type string. class lcl_amc_test_text definition FINAL create public . public section. interfaces if_amc_message_receiver_text . endclass. class lcl_amc_test_text implementation. method if_amc_message_receiver_text~receive. message = i_message. return. endmethod. endclass. |
В код ФМ нужно создать класс соединения с каналом, поставить прослушивать наш канал с обработчиком, который описан выше.
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 26 27 28 29 30 31 |
*"---------------------------------------------------------------------- *"*"Локальный интерфейс: *" IMPORTING *" VALUE(IN_TEST) TYPE STRING OPTIONAL *" EXPORTING *" VALUE(OUT_TEST) TYPE STRING *"---------------------------------------------------------------------- OUT_TEST = IN_TEST. **** слушатель data: lo_consumer type ref to if_amc_message_consumer. DATA: lo_receiver_text TYPE REF TO lcl_amc_test_text, lx_amc_error TYPE REF TO cx_amc_error. TRY. lo_consumer = cl_amc_channel_manager=>create_message_consumer( i_application_id = 'Z_CHANNEL_TEST' i_channel_id = '/teleport' ). lo_receiver_text = new #( ). lo_consumer->start_message_delivery( lo_receiver_text ). wait for messaging channels until message is not initial up to 1000 seconds. CATCH cx_amc_error INTO lx_amc_error. MESSAGE lx_amc_error->get_text( ) TYPE 'E'. OUT_TEST = lx_amc_error->get_text( ). exit. ENDTRY. message message type 'I'. |
В какой-то момент фм будет просто «висеть» и ждать либо пока сообщение не придет, либо пока не пройдет 1000 секунд. Соответственно условие для wait может быть любым логическим выражением, но время ожидания должно быть обязательно.
Пример для демонстрации готов, запускаем фм Z92_CONNECTOR, затем программу Z92_CHAT.
В окне запуска ФМ у меня показалось следующие сообщение

Как видно по скрину, канал был успешно настроен сообщение из программы пришло в ФМ.
Реализация чата
Первым делом в ФМ строку вывода сообщения заменяем на следующую
1 |
OUT_TEST = message. |
В свойствах ФМ нужно поставить «Дистанционный модуль». После этого Z92_CONNECTOR править больше не нужно.
Далее что бы добиться полноценной работы слушателя, что бы приложение не висело в ожидании, будем это ожидание выносить в отдельный процесс при помощи ключевого слова starting new task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
********* отправитель data: lo_producer_text type ref to if_amc_message_producer_text. lo_producer_text ?= cl_amc_channel_manager=>create_message_producer( i_application_id = 'Z_CHANNEL_TEST' i_channel_id = '/teleport' ). *************** * запускаем слушатель в отдельном "треде" taskname = 'CONNECTOR_' && sy-uname && sy-uzeit. call function 'Z92_CONNECTOR' starting new task taskname performing give_fm_data on end of task exceptions communication_failure = 1 system_failure = 2. * * call screen 100. |
Текст чата я буду выводить в обычный ALV, так же на экране у меня будет поле ввода сообщения с повешенным на него USER-COMMAND при нажатии на ENTER.


PBO экрана
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
types: begin of tab_s, timeMes type sy-uzeit, uname type sy-uname, messages type string, color(4), end of tab_s. data: tab type table of tab_s, grid type ref to cl_gui_alv_grid, container type ref to cl_gui_custom_container, f_catalog type lvc_t_fcat, layout type lvc_s_layo. load-of-program. perform creaty_f_catalog. FORM CREATY_F_CATALOG . perform new_field using '1' 'Время' 'TIMEMES' 'TIMS' 15 15 '' '' '' 'CHAR' '' '' changing f_catalog. perform new_field using '2' 'Пользователь' 'UNAME' 'CHAR' 15 15 '' '' '' 'CHAR' '' '' changing f_catalog. perform new_field using '3' 'Сообщение' 'messages' 'CHAR' 150 150 '' '' '' 'CHAR' '' '' changing f_catalog. ENDFORM. *&---------------------------------------------------------------------* *& Form NEW_FIELD *&---------------------------------------------------------------------* FORM NEW_FIELD USING p_num p_nam p_field p_type p_len p_outlen p_key p_edit p_f4 p_rfield p_conv p_sum changing p_f_catalog type lvc_t_fcat. DATA fcat_property TYPE lvc_s_fcat. fcat_property-col_pos = p_num. fcat_property-fieldname = p_field. fcat_property-key = p_key. fcat_property-outputlen = p_outlen. fcat_property-datatype = p_type. fcat_property-intlen = p_len. fcat_property-reptext = p_nam. fcat_property-scrtext_l = p_nam. fcat_property-scrtext_m = p_nam. fcat_property-scrtext_s = p_nam. fcat_property-just = 'X'. fcat_property-edit = p_edit. fcat_property-f4availabl = p_f4. fcat_property-ref_field = p_rfield. fcat_property-do_sum = p_sum. fcat_property-LOWERCASE = 'X'. APPEND fcat_property TO p_f_catalog. ENDFORM. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
MODULE GRID_INIT OUTPUT. if grid is initial. container = new #( 'CHAT_CONTAINER' ). grid = new #( container ). layout-zebra = abap_true. grid->set_table_for_first_display( exporting is_layout = layout changing it_outtab = tab it_fieldcatalog = f_catalog ). endif. grid->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_modified ). ENDMODULE. |
PAI экрана
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 26 27 28 29 30 31 32 |
MODULE USER_COMMAND_0100 INPUT. case sy-ucomm. when 'OUT'. leave program. when 'ENTER'. data: lx_amc_error type ref to cx_amc_error. try. data: i_message type string. i_message = sy-uname && '@' && USER_MESSAGE. lo_producer_text->send( i_message = i_message ). catch cx_amc_error into lx_amc_error. message lx_amc_error->get_text( ) type 'E'. endtry. clear: USER_MESSAGE. when 'OKCD'. perform refresh_grid. call function 'Z92_CONNECTOR' starting new task taskname performing give_fm_data on end of task exceptions communication_failure = 1 system_failure = 2. endcase. ENDMODULE. |
В ucomm ENTER по мимо сообщения, я добавляю текущее время и имя пользователя. Осталось описать логику исполнения после того, как листенер отработал т.е. сообщение пришло. У меня это подпрограмма GIVE_FM_DATA
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 26 27 28 29 30 31 |
FORM GIVE_FM_DATA using taskname. data: fm_message type string. receive results from function 'Z92_CONNECTOR' importing out_test = fm_message. if fm_message is not initial. data: tab_line type tab_s. get time. tab_line-timemes = sy-uzeit. split fm_message at '@' into tab_line-uname tab_line-messages. append tab_line to tab. endif. if lines( tab ) > 25. data end_i type i. end_i = lines( tab ) - 25. delete tab from 1 to end_i. endif. set user-command 'OKCD'. ENDFORM. |
Тут происходит следующее: сообщение приходит, сплитится и добавляется в во внутреннюю таблицу, в последней строчке листенер перезапускается. Если посмотреть на листинг по выше, реализацию user-command OKCD, то можно увидеть, что там обновляется грид и по новой запускается наш ФМ в отдельном процессе.
Если запустить это приложение в разных окнах или у разных пользователей, то можно увидеть, что отправленное сообщение отображается во всех окнах, где приложение запущено.

Исходный код
Исходный код можно найти по ссылке.