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.