Implement ADF: ABAP Daemon Framework

The example below depicts one of the possible use case of abap daemon, with the integration between interface technologies: AIF(Application Interface Framework), IDOC’s, Asynchronous RFC, Business events(workflow) & class events, Report program, email and ABAP Daemon.

Disclaimer:

  • In example, no actual document is created but used some dummy number (assuming SAP Document number) to update custom database/trigger outbound Idoc after every event occurrence.
  • Please check if AIF component AIFGEN available and administration is configured in the system.If not you can use any other interfacing technique e.g. Idoc, proxy, REST API etc.
  • ABAP Daemon uses PCP (Push channel Protocol) format(name & value pair) to transfer the message between the sessions & keep the state.To send message data to daemon JSON format(serialize/deserialize) is used.Internal table/structure are converted JSON(long string) and sent to daemon where it is again converted back to Internal table/structure from JSON. You can use any long string format(XML, XSTRING etc).
  • If email not received, please check t-code SOST.
  • To debug the daemon/AIF, you use external debugger or infinite loop (SM50 -work process debug).

To understand better, I implemented prototype based on below business scenario.

Also Read: SAP ABAP 7.5 Certification Preparation Guide

Business Scenario: General Asset Procurement Process – Example

Let suppose user/business want to procure some asset/item ( e.g. laptop/ Heavy machinery for factory). So in this process, different document are created to complete the procurement. After each documents(or events) creation/posting, I want certain custom table to be updated & outbound IDOC message should be trigger. The step of updating the table & sending IDOC will be handle by daemon instance. Below are I tried to explain how complete business process will flow for my example.

  • Business/user raises request in some portal of legacy system for procurement of asset and request details sent to S/4 HANA (1909) system via interfacing technology AIF (Application Interface Framework).
  • Upon receiving AIF message, different documents created (e.g. Internal Order, Asset Master & Purchase Requisition) will be created.The document created in this step needs to be updated in custom table(for reference) & trigger an outbound is sent.
  • Next step, Purchase Order created with reference to Purchase requisition.The Purchase order also needs to be updated in custom table & trigger an outbound is sent.
  • Next step, supplier sends physical asset. This leads good receipt in the S/4 HANA. The goods receipt details was received via AIF which results in Material document posting. The Material documents needs to be updated in custom table & trigger an outbound is sent.
  • Lastly,Supplier sends invoice via AIF which leads to purchase Invoice creation in the system in S/4 HANA. The Invoice documents needs to be updated in custom table & an outbound is sent
  • There are many add-on process to complete in asset procurement (e.g. capitalization, depreciation, deactivation), let’s not focus more subsequent business process and create technical flow using Daemon.

Each Inbound/Outbound message or document create/change/delete can be considered as event in ABAP.

The complete implementation is based on above explained business scenario.

Technical Design:

Note: Below example, implemented & tested is done S/4 HANA 1909(AS ABAP 7.54).

Here, I tried to integration between different ABAP Objects/features with ABAP Daemon:

Report Program: This is an interactive program (pop-up based) responsible for trigger of Inbound AIF message/event.

AIF(Application Interface Framework): Used for Inbound flow of message. Here I am using AIF 4.0.

IDOC: To send outbound message.

Business event linkage or Class event: To raise some custom event system & communicate will Daemon Instance.

Database Table: For staging the information.

EMAIL: To send email for notification.

Asynchronous RFC: To trigger outbound IDOC from daemon API. ABAP Daemons use an adapted non-blocking programming mode. In this programming model, most of these blocking ABAP statements, e.g. SUBMIT, CALL TRANSACTION, WAIT, CALL SCREEN, etc. are not allowed and usage will result runtime error ABAP_DAEMON_ILLEGAL_STATEMENT. Hence to avoid such possible error, asynchronous RFC was used as sometime standard FM/program contains blocking statements.

SAP Daemon Framework: For event handling. In the example , daemon API methods are responsible to update custom database table, trigger outbound IDOC & send email upon receiving message via AIF/class/business event.

Business Flow design

The complete process flow is described below:

Demonstration

Lets first show demo. All the steps in the demo are described in detail after the video:

Step 1: Create Order, Asset & Purchase Requisition

  • The report program(consider it as legacy system) will trigger Inbound AIF message. After receiving AIF message & relevant document (e.g. dummy number for internal order, Asset, PR) created.
  • Subsequently, it will also start the Daemon instance. The logic to start the daemon encapsulated inside AIF Action FM.
  • During start of Daemon, two methods (daemon API method ON_ACCEPT & ON_START) are triggered. ON_START method executed only once just after the daemon start has been accepted (ON_ACCEPT). It contains the logic to update the custom database table & trigger first outbound IDOC message with details (e.g. dummy number for internal order, Asset, PR).
  • Post processing of this step Daemon will still be active but will be Idle It can be seen in Daemon monitoring transaction code SMDAEMON.

Step 2: Create Purchase Order

  • Next step, create purchase order (dummy number) with reference to Purchase requisition created in step 1.
  • In this step, I will try to send message to Daemon instance (which is in already running & Idle state after step 1) by raising some custom events.This time API Method ON_MESSAGE will triggered which contains same logic as method ON_START i.e. to update table and trigger outbound IDOC to legacy with Purchase Order details.
  • To raise event, I have used two possible way: class and business event. Anything event typecan be used.

Step 3: Create Material Document

  • Next step, good receipts sent via AIF. This will lead to material document creation in the system.
  • To replicate this scenario report program used which will trigger an inbound AIF to create Material document (some dummy number).
  • AIF action FM will pass message to daemon instance (already running in Idle status after performing step 2). This time again API Method ON_MESSAGE will triggered which contains i.e. to update table and trigger outbound IDOC to legacy with Material Document details.

Step 4: Create Invoice document

  • Next step, invoice sent via AIF, which will result purchasing Invoice creation/posting.
  • To replicate this scenario report program will be called which will trigger an inbound AIF to create Invoice document (dummy number).
  • AIF action FM will pass message to daemon instance (already running in Idle status after performing step 3). This time again API Method ON_MESSAGE will triggered which contains to update table and trigger outbound IDOC to legacy with Invoice document details.

STEP 5: Stop Daemon instance & send email notification

  • Stop daemon:
  • During this process, Method ON_STOP will trigger which contains the logic to trigger email.
  • This will lead to email trigger in the system.

Execute the interactive report to check each step and transaction to have good understanding.

Let us start development…

The complete implementation/source code is described step by step below:

Create a staging table:

Table source code:

@EndUserText.label : 'Staging Table'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #LIMITED
define table ztest_t_aif {
  key mandt  : mandt not null;
  key field1 : zttsid not null;
  key field2 : ztts_item not null;
  field3     : ztts_io;
  field4     : ztts_pr;
  field5     : ztts_pr_itm;
  field6     : ztts_asset;
  field7     : ztts_po;
  field8     : ztts_po_itm;
  field9     : ztts_gr;
  field10    : ztts_inv;
  status     : zcomment;
} 

Create structure

@EndUserText.label : 'TEST AIF'
@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE
define structure ztest_aif {
  field1  : zttsid;
  field2  : ztts_item;
  field3  : ztts_io;
  field4  : ztts_pr;
  field5  : ztts_pr_itm;
  field6  : ztts_asset;
  field7  : ztts_po;
  field8  : ztts_po_itm;
  field9  : ztts_gr;
  field10 : ztts_inv;
  status  : char50;
}

Create table type

Source Code

Source code:
@EndUserText.label : 'Deep Structute'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_CHARACTER
define structure ztest_d_aif {
  test : ztest_aif;
}

Create & configure AIF Interface in T-code /N/AIF/CUST

Define Name space: ZIFTST(Check node in t-code: /N/AIF/CUST)

Define Interface: IF_TEST and assign deep structure created previously & mark check box ‘Move Corresponding structures’ (Check node in t-code: /N/AIF/CUST)

Define Action: AC_TEST & assign FM (Check node in t-code: /N/AIF/CUST)

Signature of both FM should same as FM /AIF/FILE_TEMPL_PROCESS

Source code of FM: ZTEST_AC_AIF

FUNCTION ztest_ac_aif
  IMPORTING
    testrun TYPE c
    sending_system TYPE /aif/aif_business_system_key OPTIONAL
  CHANGING
    data TYPE any ##ADT_PARAMETER_UNTYPED
    curr_line TYPE any ##ADT_PARAMETER_UNTYPED
    success TYPE /aif/successflag
    old_messages TYPE /aif/bal_t_msg
  TABLES
    return_tab LIKE bapiret2.

  DATA: lv_msgty TYPE sy-msgty,
        lv_msgv1 TYPE syst_msgv.
  REFRESH return_tab.
  DATA(ls_aif) = CORRESPONDING ztest_t_aif( curr_line ).
  MODIFY ztest_t_aif FROM ls_aif.
  IF sy-subrc IS INITIAL.
    lv_msgty = 'S'.
    IF ls_aif-field9 IS NOT INITIAL AND ls_aif-field10 IS INITIAL.
      lv_msgv1 = |Material Document { ls_aif-field9 } posted|.
    ELSEIF ls_aif-field9 IS NOT INITIAL AND ls_aif-field10 IS NOT INITIAL.
      lv_msgv1 = |Invoice Document { ls_aif-field10 } posted|.
    ELSE.
*--prepare messages
      return_tab[] =
      VALUE #( ( type = |S| id = |/AIF/MES| number = |000| message_v1 = |Internal Order { ls_aif-field3 } created| )
               ( type = |S| id = |/AIF/MES| number = |000| message_v1 = |Purchase Requsition/Item { ls_aif-field4 } / { ls_aif-field5 } created| )
               ( type = |S| id = |/AIF/MES| number = |000| message_v1 = |Asset { ls_aif-field6 } posted| ) ).
*
    ENDIF.
  ELSE.
    lv_msgty = 'E'.
    IF ls_aif-field9 IS NOT INITIAL AND ls_aif-field10 IS INITIAL.
      lv_msgv1 = |Material Document { ls_aif-field9 } not posted|.
    ELSEIF ls_aif-field9 IS NOT INITIAL AND ls_aif-field10 IS NOT INITIAL.
      lv_msgv1 = |Invoice Document { ls_aif-field10 } not posted|.
    ELSE.
*--prepare messages
      return_tab[] =
      VALUE #( ( type = |E| id = |/AIF/MES| number = |000| message_v1 = |Internal Order { ls_aif-field3 } not created| )
               ( type = |E| id = |/AIF/MES| number = |000| message_v1 = |Purchase Requsition/Item { ls_aif-field4 } / { ls_aif-field5 } not created| )
               ( type = |E| id = |/AIF/MES| number = |000| message_v1 = |Asset { ls_aif-field6 } not posted| ) ).
    ENDIF.
  ENDIF.
*--add message to AIF application log
  CALL FUNCTION '/AIF/UTIL_ADD_MSG'
    EXPORTING
      msgty      = lv_msgty
      msgid      = '/AIF/MES'
      msgno      = '000'
      msgv1      = lv_msgv1
    TABLES
      return_tab = return_tab.
ENDFUNCTION.

Source code of FM: ZTEST_AC_AIF_DAEMON

FUNCTION ztest_ac_aif_daemon
  IMPORTING
    testrun TYPE c
    sending_system TYPE /aif/aif_business_system_key OPTIONAL
  CHANGING
    data TYPE any ##ADT_PARAMETER_UNTYPED
    curr_line TYPE any ##ADT_PARAMETER_UNTYPED
    success TYPE /aif/successflag
    old_messages TYPE /aif/bal_t_msg
  TABLES
    return_tab LIKE bapiret2.

  DATA(ls_curr) = CONV ztest_aif( curr_line ).
  TRY.
*--convert from ABAP to JSON to get json long string
**********************************************************************
*--Long string is being converted as PCP protocol only have name value pair
*  It would be good to pass internal table/structure in message long string
*  Any other way of coversion can also be used e.g XML using CALL TRANSFORMATION etc..
**********************************************************************
      DATA(lv_json_output) = /ui2/cl_json=>serialize( data = ls_curr
                                                         compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).

*--check if any daemon already running with name ?
      IF zcl_daemon_handler=>check_daemon( daemon_class = 'ZCL_ADF'
                                           daemon_name = |TTS_{ ls_curr-field1 }_{ ls_curr-field2 }| ) EQ abap_false.
*--if not, start daemon with unique name & send payload in xstring format
        zcl_daemon_handler=>start_daemon(
                               EXPORTING i_class_name = 'ZCL_ADF'
                                         i_name  = |TTS_{ ls_curr-field1 }_{ ls_curr-field2 }|
*--Pass message(JSON)to daemon
                                         it_pcp_msg   = VALUE #( ( name = 'MESSAGE' value = lv_json_output ) ) " Pass message(JSON)to daemon
                              IMPORTING  e_setup_mode = DATA(lv_mode)
                                         e_instance_id = DATA(instance_id) ). "Daemon unqiue instance id
*--if daemon started setup mode will be 1 else start rejected
        IF lv_mode EQ 1.
          DATA(lv_msgv1) = CONV char50( |Daemon TTS_{ ls_curr-field1 }_{ ls_curr-field2 } started| ).
          DATA(lv_msgty) = CONV syst_msgty( 'S').
        ELSE.
          lv_msgv1 = CONV char50( |Daemon TTS_{ ls_curr-field1 }_{ ls_curr-field2 } start failed| ).
          lv_msgty = 'E'.
        ENDIF.
      ELSE.
        lv_msgv1 = CONV char50( |Daemon TTS_{ ls_curr-field1 }_{ ls_curr-field2 } already running...| ).
        lv_msgty = CONV syst_msgty( 'S').
*--send message(JSON) to daemon
        zcl_daemon_handler=>send_message(
                               EXPORTING i_daemon_class = CONV #( 'ZCL_ADF' )
                                         i_daemon_name  = |TTS_{ ls_curr-field1 }_{ ls_curr-field2 }|
                                         it_pcp_msg     = VALUE #( ( name = 'MESSAGE' value = lv_json_output ) ) ).
      ENDIF.

Go to Define structure mapping & Assign Action (Check node in t-code: /N/AIF/CUST)

Specify Interface engines(Check node in t-code: /N/AIF/CUST)

Now create Outbound Idoc interface:

Create Segment ZTSTSEG in T-code WE31 & set release.

Include all the fields of the structure ZTEST_AIF.

Create Basic type in WE30 & set release.

Create message type in WE81 and link message & basic type in WE82.

Create logical system TEST in BD54, Port: TEST (with RFC) in WE21 & Partner Profile in WE20 as below:

Create RFC enable FM ZTEST_DAEMON_OUTBOUND_IDOC_RFC

FM Source code:

Please check restriction in my previous blog.

FUNCTION ztest_daemon_outbound_idoc_rfc
  IMPORTING
    VALUE(i_param) TYPE ztest_d_aif
  EXPORTING
    VALUE(e_idoc_no) TYPE edi_docnum.

  DATA: ls_edidc   TYPE edidc,
        lt_edidc   TYPE STANDARD TABLE OF edidc,
        lt_edidd   TYPE STANDARD TABLE OF edidd,
        ls_segment TYPE ztstseg,
        lt_return  TYPE STANDARD TABLE OF bapiret2.
*--prepare segment fields
  ls_segment = CORRESPONDING #( i_param-test ).
*--prepare IDOC data record
  lt_edidd = VALUE #( ( mandt = sy-mandt segnum = '1' segnam = |ZTSTSEG| sdata = ls_segment  ) ).
*--prepare idoc control record
  ls_edidc = VALUE #( rcvpor = 'TEST' mestyp = |ZTSTMSGTY| idoctp = |ZTSTSEG|
                      rcvprt = |LS|  rcvprn  = |TEST | sndprn = |ORDCLNT623| sndprt = |LS| ).
*--trigger Outbound IDOC
  CALL FUNCTION 'MASTER_IDOC_DISTRIBUTE'
    EXPORTING
      master_idoc_control            = ls_edidc
    TABLES
      communication_idoc_control     = lt_edidc
      master_idoc_data               = lt_edidd
    EXCEPTIONS
      error_in_idoc_control          = 1
      error_writing_idoc_status      = 2
      error_in_idoc_data             = 3
      sending_logical_system_unknown = 4
      OTHERS                         = 5.

  IF sy-subrc IS INITIAL.
    DATA(ls_test) = CORRESPONDING ztest_t_aif( ls_segment ).
    ls_test-status =  e_idoc_no = lt_edidc[ 1 ]-docnum.
    ls_test-status =  |IDOC Number: { e_idoc_no }|.
    MODIFY ztest_t_aif FROM ls_test.
*--force release of IDOC lock to avoid status 30
    CALL FUNCTION 'DB_COMMIT'.
    CALL FUNCTION 'DEQUEUE_ALL'.
    COMMIT WORK.
  ENDIF.
ENDFUNCTION.

Now Implement ABAP Daemon Framework

To implement ADF, class CL_ABAP_DAEMON_EXT_BASE needs to be inherited. This class implements interface: IF_ABAP_DAEMON_EXTENSION (ABAP Daemon framework: Event handler). The inherited methods needs to re-defined.

Class source code:

Note: Add your email id in the method ON_STOP & ON_ERROR

class ZCL_ADF definition
  public
  inheriting from CL_ABAP_DAEMON_EXT_BASE
  final
  create public .
public section.
  METHODS: if_abap_daemon_extension~on_error REDEFINITION,
           if_abap_daemon_extension~on_message REDEFINITION,
           if_abap_daemon_extension~on_restart REDEFINITION,
           if_abap_daemon_extension~on_server_shutdown REDEFINITION,
           if_abap_daemon_extension~on_accept REDEFINITION,
           if_abap_daemon_extension~on_start REDEFINITION,
           if_abap_daemon_extension~on_stop REDEFINITION,
           if_abap_daemon_extension~on_system_shutdown REDEFINITION,
           if_abap_daemon_extension~on_before_restart_by_system REDEFINITION.
protected section.
private section.
ENDCLASS.

CLASS ZCL_ADF IMPLEMENTATION.
  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_DAEMON_HANDLER' ).

        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: error handling, e.g. write error log!
        e_setup_mode = co_setup_mode-reject.
    ENDTRY.
  ENDMETHOD.

  METHOD IF_ABAP_DAEMON_EXTENSION~ON_BEFORE_RESTART_BY_SYSTEM.

  ENDMETHOD.

  METHOD if_abap_daemon_extension~on_error.
    DATA(lo_call_info) = i_context->get_start_caller_info( ).
    DATA: lt_mailsubject     TYPE sodocchgi1.
    DATA: lt_mailrecipients  TYPE STANDARD TABLE OF somlrec90 .
    DATA: lt_mailtxt         TYPE STANDARD TABLE OF soli.
* Recipients
    lt_mailrecipients = VALUE #( ( rec_type = |U| receiver = <put your email ID> ) ).
* Subject.
    lt_mailsubject = VALUE #( obj_name = |TEST'| obj_langu = sy-langu obj_descr = |DAEMON: { lo_call_info-name } stopped| ) .
* Mail Contents
    lt_mailtxt = VALUE #( ( |DAEMON: { lo_call_info-name } stopped| )
                          ( )
                          ( |REASON CODE: { I_CODE }| )
                          ( |REASON     : { I_REASON }| ) ).

* Send Mail
    CALL FUNCTION 'SO_NEW_DOCUMENT_SEND_API1'
      EXPORTING
        document_data              = lt_mailsubject
      TABLES
        object_content             = lt_mailtxt
        receivers                  = lt_mailrecipients
      EXCEPTIONS
        too_many_receivers         = 1
        document_not_sent          = 2
        document_type_not_exist    = 3
        operation_no_authorization = 4
        parameter_error            = 5
        x_error                    = 6
        enqueue_error              = 7
        OTHERS                     = 8.
    IF sy-subrc EQ 0.
      COMMIT WORK.
    ENDIF.
  ENDMETHOD.

  METHOD if_abap_daemon_extension~on_message.
    DATA: ls_test_t TYPE ztest_t_aif,
          ls_data   TYPE ztest_d_aif.
    DATA lt_fields  TYPE if_ac_message_type_pcp=>tt_pcp_fields.
    DATA(lo_parameter) = i_context->get_start_parameter( ).
    DATA(lv_text) = lo_parameter->get_text( ).
    DATA(lo_call_info) = i_context->get_start_caller_info( ).
    DATA(lo_application_para) = i_context->get_application_parameter( ).
    DATA(instance_id) = i_context->get_instance_id( ).
*--retrieve PCP Message
        i_message->get_fields( CHANGING c_fields = lt_fields  ).

*--retrive message
    ASSIGN lt_fields[ name = |MESSAGE| ] TO FIELD-SYMBOL(<ls_field>).
    IF <ls_field> IS ASSIGNED.
**********************************************************************
*--deserialize from JSON long string to ABAP
**********************************************************************
      /ui2/cl_json=>deserialize(
                 EXPORTING
                   json = CONV #( <ls_field>-value )
                   pretty_name = /ui2/cl_json=>pretty_mode-camel_case
                 CHANGING data = ls_data-test ).

      IF ls_data-test IS NOT INITIAL.
        ls_test_t = CORRESPONDING #( ls_data-test ).
*--update table
        MODIFY  ztest_t_aif FROM ls_test_t.
        IF sy-subrc IS INITIAL.
          COMMIT WORK.
*---trigger outbound IDOC asynchronously
          CALL FUNCTION 'ZTEST_DAEMON_OUTBOUND_IDOC_RFC'
            STARTING NEW TASK 'NTASK' DESTINATION 'NONE'
            EXPORTING
              i_param = ls_data.
        ENDIF.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD IF_ABAP_DAEMON_EXTENSION~ON_RESTART.

  ENDMETHOD.

  METHOD IF_ABAP_DAEMON_EXTENSION~ON_SERVER_SHUTDOWN.

  ENDMETHOD.

  METHOD if_abap_daemon_extension~on_start.
    DATA: ls_test_t TYPE ztest_t_aif,
          ls_data   TYPE ztest_d_aif.
    DATA lt_fields  TYPE if_ac_message_type_pcp=>tt_pcp_fields.
    DATA(lo_parameter) = i_context->get_start_parameter( ).
    DATA(lv_text) = lo_parameter->get_text( ).
    DATA(lo_call_info) = i_context->get_start_caller_info( ).
    DATA(lo_application_para) = i_context->get_application_parameter( ).
    DATA(instance_id) = i_context->get_instance_id( ).

*--retrieve pcp MESSAGE
    lo_parameter->get_fields( CHANGING c_fields = lt_fields  ).

*--retrive message
    ASSIGN lt_fields[ name = |MESSAGE| ] TO FIELD-SYMBOL(<ls_field>).
    IF <ls_field> IS ASSIGNED.
**********************************************************************
*--deserialize from JSON long string to ABAP
**********************************************************************
      /ui2/cl_json=>deserialize(
                 EXPORTING
                   json = CONV #( <ls_field>-value )
                   pretty_name = /ui2/cl_json=>pretty_mode-camel_case
                 CHANGING data = ls_data-test ).

      IF ls_data-test IS NOT INITIAL.
        ls_test_t = CORRESPONDING #( ls_data-test ).
*--update table
        MODIFY  ztest_t_aif FROM ls_test_t.
        IF sy-subrc IS INITIAL.
          COMMIT WORK.
*---trigger outbound IDOC asynchronously
          CALL FUNCTION 'ZTEST_DAEMON_OUTBOUND_IDOC_RFC'
            STARTING NEW TASK 'NTASK' DESTINATION 'NONE'
            EXPORTING
              i_param = ls_data.
        ENDIF.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD if_abap_daemon_extension~on_stop.
    DATA lt_fields  TYPE if_ac_message_type_pcp=>tt_pcp_fields.
    DATA(lo_parameter) = i_context->get_start_parameter( ).
    DATA(lv_text) = lo_parameter->get_text( ).
    i_message->get_fields( CHANGING c_fields = lt_fields  ).

    DATA(lo_call_info) = i_context->get_start_caller_info( ).
    DATA(lo_application_para) = i_context->get_application_parameter( ).
    DATA(instance_id) = i_context->get_instance_id( ).

    DATA: lt_mailsubject     TYPE sodocchgi1.
    DATA: lt_mailrecipients  TYPE STANDARD TABLE OF somlrec90 .
    DATA: lt_mailtxt         TYPE STANDARD TABLE OF soli.
* Recipients
    lt_mailrecipients = VALUE #( ( rec_type = |U| receiver = <put your email ID> ) ).
* Subject.
    lt_mailsubject = VALUE #( obj_name = |TEST'| obj_langu = sy-langu obj_descr = |DAEMON: { lo_call_info-name } stopped| ) .
* Mail Contents
    lt_mailtxt = VALUE #( ( |DAEMON: { lo_call_info-name } stopped| ) ).

* Send Mail
    CALL FUNCTION 'SO_NEW_DOCUMENT_SEND_API1'
      EXPORTING
        document_data              = lt_mailsubject
      TABLES
        object_content             = lt_mailtxt
        receivers                  = lt_mailrecipients
      EXCEPTIONS
        too_many_receivers         = 1
        document_not_sent          = 2
        document_type_not_exist    = 3
        operation_no_authorization = 4
        parameter_error            = 5
        x_error                    = 6
        enqueue_error              = 7
        OTHERS                     = 8.
    IF sy-subrc EQ 0.
      COMMIT WORK.
    ENDIF.
  ENDMETHOD.

  METHOD IF_ABAP_DAEMON_EXTENSION~ON_SYSTEM_SHUTDOWN.

  ENDMETHOD.
ENDCLASS.

Create Handler class as below:

Note: These interfaces implementation are optional. I have implemented as I am using Business Workflow Event linkage in my example.

Class source code

"! <p class="shorttext synchronized" lang="en">Daemon Handler</p>
class ZCL_DAEMON_HANDLER definition
  public
  final
  create public .

public section.

  interfaces BI_OBJECT .
  interfaces BI_PERSISTENT .
  interfaces IF_WORKFLOW .
  interfaces BI_EVENT_HANDLER_STATIC .

  types:
*--types
    BEGIN OF ty_pcp_msg,
        i_field   TYPE string,
        i_value   TYPE string,
        i_message TYPE string,
      END OF ty_pcp_msg .
  types:
*--table type
    tt_pcp_msg TYPE TABLE OF  ty_pcp_msg .

  constants:
*--Constant
    BEGIN OF co_session_priority,
        high   TYPE i VALUE 0,
        normal TYPE i VALUE 1,
        low    TYPE i VALUE 2,
      END OF co_session_priority .

  class-events SEND_MESSAGE_DAEMON
    exporting
      value(IT_PCP_MESSAGE) type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS .
  class-events WF_SEND_MESSAGE_DAEMON
    exporting
      value(IT_PCP_MESSAGE) type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS .

  methods CONSTRUCTOR .
  class-methods START_DAEMON
    importing
      !I_CLASS_NAME type IF_ABAP_DAEMON_TYPES=>TY_ABAP_DAEMON_CLASS_NAME
      !I_DESTINATION type IF_ABAP_DAEMON_TYPES=>TY_ABAP_DAEMON_DESTINATION default 'NONE'
      !I_NAME type IF_ABAP_DAEMON_TYPES=>TY_ABAP_DAEMON_NAME
      value(I_PRIORITY) type IF_ABAP_DAEMON_TYPES=>TY_ABAP_DAEMON_PRIORITY default CO_SESSION_PRIORITY-NORMAL
      !IT_PCP_MSG type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS optional
    exporting
      !E_SETUP_MODE type I
      !E_INSTANCE_ID type IF_ABAP_DAEMON_TYPES=>TY_ABAP_DAEMON_INSTANCE_ID
    raising
      CX_ABAP_DAEMON_ERROR .
  class-methods SEND_MESSAGE
    importing
      !IT_PCP_MSG type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS
      !I_DAEMON_CLASS type ABAP_DAEMON_CLASS_NAME
      !I_DAEMON_NAME type ABAP_DAEMON_NAME
      !I_INSTANCE_ID type ABAP_DAEMON_INSTANCE_ID optional
    raising
      CX_ABAP_DAEMON_ERROR .
  class-methods STOP_DAEMON
    importing
      !I_DAEMON_CLASS type ABAP_DAEMON_CLASS_NAME
      !I_DAEMON_NAME type ABAP_DAEMON_NAME
      !I_INSTANCE_ID type ABAP_DAEMON_INSTANCE_ID optional
    raising
      CX_ABAP_DAEMON_ERROR .
  class-methods EVENT_HANDLER
    for event SEND_MESSAGE_DAEMON of ZCL_DAEMON_HANDLER .
  class-methods RAISE_EVENT
    importing
      !IT_PCP_MESSAGE type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS .
  class-methods CREATE_PCP_MSG
    importing
      !IT_PCP_MSG type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS optional
    returning
      value(R_MESSAGE) type ref to IF_AC_MESSAGE_TYPE_PCP
    raising
      CX_AC_MESSAGE_TYPE_PCP_ERROR .
  class-methods GET_DAEMON_INFO
    importing
      !DAEMON_CLASS type ABAP_DAEMON_CLASS_NAME
      !DAEMON_NAME type ABAP_DAEMON_NAME
    returning
      value(R_INSTANCE_ID) type ABAP_DAEMON_INSTANCE_ID
    raising
      CX_ABAP_DAEMON_ERROR .
  class-methods CHECK_DAEMON
    importing
      !DAEMON_CLASS type ABAP_DAEMON_CLASS_NAME
      !DAEMON_NAME type ABAP_DAEMON_NAME
    returning
      value(R_BOOLEAN) type ABAP_BOOL .
  class-methods RAISE_CLASS_EVENT
    importing
      value(IT_PCP_MESSAGE) type IF_AC_MESSAGE_TYPE_PCP=>TT_PCP_FIELDS .
  class-methods HANDLE_CLASS_EVENT
    for event SEND_MESSAGE_DAEMON of ZCL_DAEMON_HANDLER
    importing
      !IT_PCP_MESSAGE .
  PROTECTED SECTION.

private section.

  class-data INSTANCE_ID type ABAP_DAEMON_INSTANCE_ID .
  class-data DAEMON_HANDLE type ref to IF_ABAP_DAEMON_HANDLE .
ENDCLASS.

CLASS ZCL_DAEMON_HANDLER IMPLEMENTATION.
  METHOD bi_event_handler_static~on_event.
    DATA: it_pcp_message TYPE if_ac_message_type_pcp=>tt_pcp_fields.
    event_container->get(  EXPORTING name = 'IT_PCP_MESSAGE' IMPORTING value = it_pcp_message ).
    IF zcl_daemon_handler=>check_daemon( daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                        daemon_name = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value ) ) EQ abap_false.
      zcl_daemon_handler=>start_daemon(
                                   EXPORTING i_class_name  = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                             i_name        = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value )
                                  IMPORTING  e_setup_mode  = DATA(lv_mode)
                                             e_instance_id = DATA(instance_id) ).
      IF lv_mode EQ 1.
        send_message( EXPORTING i_daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                i_daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value )
                                i_instance_id  = get_daemon_info( EXPORTING daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                                                            daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value ) )
                                it_pcp_msg     = it_pcp_message ).
      ENDIF.
    ELSE.
      send_message( EXPORTING i_daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                              i_daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value )
                              i_instance_id  = get_daemon_info( EXPORTING daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                                                          daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value ) )
                              it_pcp_msg     = it_pcp_message ).
    ENDIF.
  ENDMETHOD.

  METHOD check_daemon.
    r_boolean =   COND #( WHEN get_daemon_info( EXPORTING daemon_class = daemon_class daemon_name = daemon_name ) IS NOT INITIAL THEN abap_true ).
  ENDMETHOD.

  METHOD create_pcp_msg.
    TRY.
        IF it_pcp_msg[] IS SUPPLIED.
          DATA(lo_pcp) = cl_ac_message_type_pcp=>create( ).
          LOOP AT it_pcp_msg ASSIGNING FIELD-SYMBOL(<ls_pcp_msg>).
*--set PCP field value pair
            IF <ls_pcp_msg>-name IS NOT INITIAL AND <ls_pcp_msg>-value IS NOT INITIAL.
              lo_pcp->set_field( i_name = <ls_pcp_msg>-name   i_value = <ls_pcp_msg>-value ).
            ENDIF.
*--set PCP message
*            IF <ls_pcp_msg>-i_message IS NOT INITIAL.
*              lo_pcp->set_text(  <ls_pcp_msg>-i_message  ).
*            ENDIF.
          ENDLOOP.
          r_message = lo_pcp.
        ENDIF.
      CATCH cx_ac_message_type_pcp_error.
    ENDTRY.
  ENDMETHOD.

  method EVENT_HANDLER.
  endmethod.

  METHOD get_daemon_info.
    TRY.
        DATA(daemon_info) = cl_abap_daemon_client_manager=>get_daemon_info( daemon_class ).
        instance_id = r_instance_id =  VALUE #( daemon_info[ name = daemon_name ]-instance_id ).
      CATCH cx_root.
        clear: instance_id, r_instance_id.
    ENDTRY.
  ENDMETHOD.
  METHOD handle_class_event.
    IF zcl_daemon_handler=>check_daemon( daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                        daemon_name = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value ) ) EQ abap_false.
      zcl_daemon_handler=>start_daemon(
                                   EXPORTING i_class_name  = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                             i_name        = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value )
                                  IMPORTING  e_setup_mode  = DATA(lv_mode)
                                             e_instance_id = DATA(instance_id) ).
      IF lv_mode EQ 1.
        send_message( EXPORTING i_daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                i_daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value )
                                i_instance_id  = get_daemon_info( EXPORTING daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                                                            daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value ) )
                                it_pcp_msg     = it_pcp_message ).
      ENDIF.
    ELSE.
      send_message( EXPORTING i_daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                              i_daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value )
                              i_instance_id  = get_daemon_info( EXPORTING daemon_class = CONV #( it_pcp_message[ name = |DAEMON_CLASS| ]-value )
                                                                          daemon_name  = CONV #( it_pcp_message[ name = |DAEMON_NAME| ]-value ) )
                              it_pcp_msg     = it_pcp_message ).
    ENDIF.
  ENDMETHOD.

  METHOD RAISE_CLASS_EVENT.
    DATA ls_handler TYPE REF TO zcl_daemon_handler.
    SET HANDLER zcl_daemon_handler=>handle_class_event.
    RAISE EVENT send_message_daemon
      EXPORTING
        it_pcp_message = it_pcp_message.
  ENDMETHOD.

  METHOD raise_event.
    TRY.
      DATA(lr_container) = cl_swf_evt_event=>get_event_container( EXPORTING
                                                                        im_objcateg = cl_swf_evt_event=>mc_objcateg_cl
                                                                        im_objtype  = 'ZCL_DAEMON_HANDLER'
                                                                        im_event    = 'WF_SEND_MESSAGE_DAEMON' ).
      lr_container->set( name = 'IT_PCP_MESSAGE' value = it_pcp_message ).

        CALL METHOD cl_swf_evt_event=>raise
          EXPORTING
            im_objcateg = cl_swf_evt_event=>mc_objcateg_cl
            im_objtype  = 'ZCL_DAEMON_HANDLER'
            im_event    = 'WF_SEND_MESSAGE_DAEMON'
            im_objkey   = 'WBV'
            im_event_container = lr_container.

        CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
          EXPORTING
            wait = abap_true.
*     IMPORTING
*       RETURN        =
      CATCH cx_root INTO DATA(lo_err).
        MESSAGE lo_err->get_text( ) TYPE 'E'.
    ENDTRY.
  ENDMETHOD.

  METHOD send_message.

    IF i_instance_id IS SUPPLIED.
      daemon_handle = cl_abap_daemon_client_manager=>attach( i_instance_id ).
    ELSE.
      daemon_handle = cl_abap_daemon_client_manager=>attach( get_daemon_info( daemon_class = i_daemon_class
                                                                              daemon_name  = i_daemon_name ) ).
    ENDIF.
    IF daemon_handle IS INITIAL.
      RAISE EXCEPTION TYPE cx_abap_daemon_error
        EXPORTING
          textid = cx_abap_daemon_error=>action_not_permitted.
    ENDIF.
    TRY.
        daemon_handle->send( create_pcp_msg( it_pcp_msg = it_pcp_msg ) ).
      CATCH cx_ac_message_type_pcp_error.
        "handle exception
    ENDTRY.
  ENDMETHOD.

  METHOD start_daemon.
    TRY.
        cl_abap_daemon_client_manager=>start(
          EXPORTING
            i_class_name  = i_class_name
            i_destination = i_destination
            i_name        = i_name
            i_priority    = i_priority
            i_parameter   = create_pcp_msg( it_pcp_msg = it_pcp_msg )
          IMPORTING
            e_setup_mode  = e_setup_mode
            e_instance_id = e_instance_id ).
      CATCH cx_root.

    ENDTRY.
  ENDMETHOD.

  METHOD stop_daemon.
    TRY.
        IF i_instance_id IS SUPPLIED.
          cl_abap_daemon_client_manager=>stop( i_instance_id ).
        ELSE.
          cl_abap_daemon_client_manager=>stop( get_daemon_info( daemon_class = i_daemon_class daemon_name = i_daemon_name ) ).
        ENDIF.
      CATCH cx_ac_message_type_pcp_error.
        "handle exception
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

Activate Business event linkage in T-code: SWE2

Report program: ZTEST_AIF

*&---------------------------------------------------------------------*
*& Report ZTEST_AIF
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT ztest_aif.
TABLES : ztest_t_aif.
INCLUDE <icon>.
DATA: ls_aif     TYPE ztest_d_aif,
      ls_aif_out TYPE STANDARD TABLE OF ztest_d_aif.
DATA: ok_pushbuttontext TYPE svalbutton-buttontext,
      icon_ok_push      TYPE icon-name,
      returncode        TYPE char1,
      icon_button_1     LIKE icon-name,
      icon_button_2     LIKE icon-name.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
  PARAMETERS : p_aif  RADIOBUTTON GROUP rb1 DEFAULT 'X' USER-COMMAND cmd,
               p_stop RADIOBUTTON GROUP rb1.
SELECTION-SCREEN END OF BLOCK b1.
SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-002.
  PARAMETERS : field1 TYPE zttsid MODIF ID sc1,
               field2 TYPE ztts_item MODIF ID sc1.
  PARAMETERS: p_dname TYPE abap_daemon_name MODIF ID sc2,
              p_dintc TYPE abap_daemon_instance_id MODIF ID sc2.
SELECTION-SCREEN END OF BLOCK b2.

INITIALIZATION.
*--Request number & item
  field1 = CONV #( sy-datum ).
  field2 = CONV #( sy-uzeit ).

AT SELECTION-SCREEN OUTPUT.
*----------------------------------------------
  LOOP AT SCREEN.
    IF p_aif IS NOT INITIAL.
      IF screen-group1 = |SC1|.
        screen-active = |1|.
      ENDIF.
      IF screen-group1 = |SC2|.
        screen-active = |0|.
      ENDIF.
    ELSE.
      IF screen-group1 = |SC1|.
        screen-active = |0|.
      ENDIF.
      IF screen-group1 = |SC2|.
        screen-active = |1|.
      ENDIF.
    ENDIF.
    MODIFY SCREEN.
  ENDLOOP.
*----------------------------------------------
START-OF-SELECTION.
  DATA(lt_components) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( |ZTEST_AIF| ) )->components .
  IF  p_aif IS NOT INITIAL AND
    ( field1 IS INITIAL OR field2 IS INITIAL ).
    MESSAGE 'Fill out all required entry for field1/2' TYPE 'S' DISPLAY LIKE 'E'.
    STOP.
  ENDIF.
  IF  p_stop IS NOT INITIAL AND
    ( p_dname IS INITIAL OR p_dintc IS INITIAL ).
    MESSAGE 'Fill either daemon name or daemon instance id' TYPE 'S' DISPLAY LIKE 'E'.
    STOP.
  ENDIF.
*--prepare aif meesage
  IF p_aif IS NOT INITIAL .
    CLEAR ls_aif.
    ls_aif-test-field1 = field1.
    ls_aif-test-field2 = field2.
*--Random Dummy internal order number(Covert INT to CHAR)
    ls_aif-test-field3 = |{ CONV char50( cl_abap_random_int=>create( seed = 10000 min = 10000 max = 19999 )->get_next( ) ) WIDTH = 10 ALPHA = IN } |.
*--Random Dummy Purchase Requisition(Covert INT to CHAR)
    ls_aif-test-field4 = |{ CONV char50( cl_abap_random_int=>create( seed = 20000 min = 20000 max = 29999 )->get_next( ) ) WIDTH = 10 ALPHA = IN } |.
*--RandomDummy Purchase Requisition Item(Covert INT to CHAR)
    ls_aif-test-field5 = |{ CONV char50( cl_abap_random_int=>create( seed = 10 min = 10 max = 999 )->get_next( ) )  WIDTH = 5 ALPHA = IN } |.
*--Random Dummy Asset(Covert INT to CHAR)
    ls_aif-test-field6 = |{ CONV char50( cl_abap_random_int=>create( seed = 30000 min = 30000 max = 39999 )->get_next( ) ) WIDTH = 10 ALPHA = IN }|.
*--trigger Inbound AIF
    /aif/cl_enabler_xml=>transfer_to_aif( EXPORTING  is_any_structure =  ls_aif IMPORTING  ev_msgguid = DATA(lv_guid) ).
*--prepare pop-up attributes
    DATA(popup_title)        = CONV char30( |Send Message to Daemon| ).
    DATA(first_pushbutton)   = CONV svalbutton-buttontext( |VIA BO/WF EVENT| ).
    DATA(quickinfo_button_1) = CONV smp_dyntxt-text( |Send Message VIA WF Event| ).
    DATA(second_pushbutton)  = CONV svalbutton-buttontext( |VIA CLASS EVENT| ).
    DATA(quickinfo_button_2) = CONV smp_dyntxt-text( |Send Message VIA Class Event| ).
    DATA(quickinfo_ok_push)  = CONV smp_dyntxt-text( |Ok| ).
    DO.
*--Fields to be displayed in the Pop-up
      DATA(lv_index) = sy-index.
      CASE lv_index.
        WHEN 1.
          DATA(lt_fields) = VALUE ty_sval(
                            ( tabname = |ZTEST_T_AIF| fieldname = |FIELD7| value =  ztest_t_aif-field7  fieldtext  = |Purchase Order| field_obl  =  abap_true )
                            ( tabname = |ZTEST_T_AIF| fieldname = |FIELD8| value =  ztest_t_aif-field8  fieldtext  = |PO Item Number| field_obl  =  abap_true ) ).
        WHEN 2.
*--pop-up push button 1  name
          first_pushbutton   = CONV svalbutton-buttontext( |Trigger I/B AIF- GR| ).
*--pop-up push button 1 quickinfo
          quickinfo_button_1 = CONV smp_dyntxt-text( |Trigger Inbound AIF-GR|  ).
*--no second button
          CLEAR: second_pushbutton, quickinfo_button_2.
          REFRESH lt_fields.
*--prepare pop-up screen field
          lt_fields = VALUE ty_sval(
                ( tabname = |ZTEST_T_AIF| fieldname = |FIELD9| value =  ztest_t_aif-field9  fieldtext  = |Material Document| field_obl  =  abap_true ) ).
        WHEN 3.
*--pop-up push button 1 name
          first_pushbutton   = CONV svalbutton-buttontext( |Trigger I/B AIF-Invoice| ).
*--pop-up push button 1 quick info
          quickinfo_button_1 = CONV smp_dyntxt-text( |Trigger Inbound AIF-Invoice|  ).
*--no second button
          CLEAR: second_pushbutton, quickinfo_button_2.
          REFRESH lt_fields.
*--prepare pop-up screen field
          lt_fields = VALUE ty_sval(
                ( tabname = |ZTEST_T_AIF| fieldname = |FIELD10| value =  ztest_t_aif-field10  fieldtext  = |Invoice Number| field_obl  =  abap_true ) ).
        WHEN OTHERS.
*--check if daemon still running ?
          IF zcl_daemon_handler=>check_daemon( daemon_class = 'ZCL_ADF'
                                               daemon_name = |TTS_{ field1 }_{ field2 }| ) EQ abap_true.

            DATA(lv_line) = |Stop Daemon instanace: TTS_{ field1 }_{ field2 } ?|.
*--Information popup
            CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
              EXPORTING
                titel     = 'Stop Daemon'
                textline1 = lv_line.
*--stop daemon
            zcl_daemon_handler=>stop_daemon( EXPORTING i_daemon_class = 'ZCL_ADF'
                                                             i_daemon_name = |TTS_{ field1 }_{ field2 }| ).
          ENDIF.
*--exit from infinite loop.
          EXIT.
      ENDCASE.
*--screen pop-up for input
      CALL FUNCTION 'POPUP_GET_VALUES_USER_BUTTONS'
        EXPORTING
          popup_title        = popup_title
          programname        = 'ZTEST_AIF'
          formname           = 'HANDLE_CODE'
          ok_pushbuttontext  = ok_pushbuttontext
          icon_ok_push       = icon_ok_push
          quickinfo_ok_push  = quickinfo_ok_push
          first_pushbutton   = first_pushbutton
          icon_button_1      = icon_button_1
          quickinfo_button_1 = quickinfo_button_1
          second_pushbutton  = second_pushbutton
          icon_button_2      = icon_button_2
          quickinfo_button_2 = quickinfo_button_2
        IMPORTING
          returncode         = returncode
        TABLES
          fields             = lt_fields.
      IF returncode EQ 'A'.
        IF zcl_daemon_handler=>check_daemon( EXPORTING daemon_class = 'ZCL_ADF'
                                                       daemon_name = |TTS_{ field1 }_{ field2 }| ) EQ abap_true.
          lv_line = |Stop Daemon instanace: TTS_{ field1 }_{ field2 } ?|.
*--Information popup
          CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
            EXPORTING
              titel     = 'Stop Daemon'
              textline1 = lv_line.
          zcl_daemon_handler=>stop_daemon( EXPORTING i_daemon_class = 'ZCL_ADF'
                                                     i_daemon_name = |TTS_{ field1 }_{ field2 }| ).
        ENDIF.
*--exit from infinite loop.
        EXIT.
      ENDIF.
    ENDDO.
  ELSEIF p_stop IS NOT INITIAL.
*--stop daemon
    zcl_daemon_handler=>stop_daemon( EXPORTING i_daemon_class = 'ZCL_ADF'
                                               i_daemon_name  = p_dname
                                               i_instance_id  = p_dintc ).
  ENDIF.

FORM handle_code TABLES   fields STRUCTURE sval
                 USING    code
                 CHANGING error  STRUCTURE svale show_popup.

  CASE code.
      LOOP AT fields ASSIGNING FIELD-SYMBOL(<ls_fields>).
*--dynamically assign screen input value to structure components
        ASSIGN COMPONENT line_index( lt_components[ name = <ls_fields>-fieldname ] )
        OF STRUCTURE ls_aif-test TO FIELD-SYMBOL(<fs_comp>).
        IF <fs_comp> IS ASSIGNED.
          <fs_comp> = <ls_fields>-value.
        ENDIF.
      ENDLOOP.
*--convert from ABAP to JSON to get json long string
**********************************************************************
*--Long string is being converted as PCP protocol only have name value pair
*  It would be good to pass internal table/structure in message long string
*  Any other way of coversion can also be used e.g XML using CALL TRANSFORMATION etc..
**********************************************************************
      DATA(lv_json_output) = /ui2/cl_json=>serialize( data = ls_aif-test
                                                         compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
    WHEN 'COD1'.
      IF lv_index EQ 1.
*--Send Message Via WF Event
        zcl_daemon_handler=>raise_class_event(
        it_pcp_message = VALUE #( ( name = |MESSAGE|  value = lv_json_output )
                                  ( name = |DAEMON_NAME|   value = |TTS_{ field1 }_{ field2 }| )
                                  ( name = |DAEMON_CLASS|  value = |ZCL_ADF| ) ) ) .
      ELSE.
*--trigger Inbound AIF
        /aif/cl_enabler_xml=>transfer_to_aif( EXPORTING  is_any_structure =  ls_aif IMPORTING  ev_msgguid = lv_guid ).
      ENDIF.
    WHEN 'COD2'.
      IF lv_index EQ 1.
*--Send Message Via Class Event
        zcl_daemon_handler=>raise_class_event(
        it_pcp_message = VALUE #( ( name = |MESSAGE|  value = lv_json_output )
                                  ( name = |DAEMON_NAME|   value = |TTS_{ field1 }_{ field2 }| )
                                  ( name = |DAEMON_CLASS|  value = |ZCL_ADF| ) ) ).
      ELSE.
*--trigger Inbound AIF
        /aif/cl_enabler_xml=>transfer_to_aif( EXPORTING  is_any_structure =  ls_aif IMPORTING  ev_msgguid = lv_guid ).
      ENDIF.
    WHEN OTHERS.
*--Do nothing
  ENDCASE.

ENDFORM.

Leave a Reply

Your email address will not be published. Required fields are marked *