This is the third part of my blog series about HCP, ABAP and websocket. You can fiend the first part (including the list of parts) here: HCP, ABAP and websocket part 1.
In this part,things will start to get more interesting: this week we aim to propagate the changes (deleted rows) from the UI5 application (from last week) to a classic dynpro and vice versa. The source code for this week is in this commit: ebe3120
The ABAP Report
I started by making a report. I just declared a table of rows like the DB table structure and created a simple procedure (or it could also be a local class method) to fill in the table by doing a SELECT on the DB: I called it load_data.
FORM load_data. CLEAR: gt_products. SELECT * FROM zdemo_epm_ws_prd INTO TABLE gt_products. ENDFORM.
After this, I just created a screen inside this report; the screen will contain only one custom control (I named it ALV). A new GUI status should also be made, with the back / cancel / exit buttons mapped to a simple ‘EXIT’ command and another button for the “delete selected row” operation (command = ‘DELETE’).
In the PBO module I added some simple code for initializing a cl_gui_alv_grid object (which will be displayed in the custom control) and a call to the load_data procedure.
In the PAI module, I wrote code for exiting the app (in case the ‘EXIT’ command was given) and for deleting a row from the DB and reloading the data (if ‘DELETE’ was given).
MODULE user_command_1000 INPUT. CASE gv_ok_code. WHEN 'EXIT'. LEAVE PROGRAM. WHEN 'DELETE'. gr_alv_ctrl->get_selected_rows( IMPORTING et_index_rows = DATA(lt_sel_rows) ). IF lines( lt_sel_rows ) > 0. DATA(ls_product) = gt_products[ lt_sel_rows[ 1 ]-index ]. DELETE zdemo_epm_ws_prd FROM ls_product. DELETE gt_products INDEX lt_sel_rows[ 1 ]-index. ELSE. MESSAGE 'No line is selected.' TYPE 'W'. ENDIF. ENDCASE. ENDMODULE.
At this point, the report can be tested: it should display all the data from the tables and should delete the selected row when we press the delete button.
Establishing the communication
So, up until now, we have a UI5 application which can respond dynamically to changes to the data done by itself and a ABAP report which doesn’t respond dynamically at all.
First let’s look into the ABAP –> UI5 scenario: when we delete a row on the dynpro screen, it should be shown on the UI5 application. This is not that complicated to do. Firstly, I made a new procedure to send a refresh message on the AMC. I also made sure to add the report to the list of authorized programs in the AMC.
FORM send_refresh. DATA: lo_producer TYPE REF TO if_amc_message_producer_text. TRY. lo_producer ?= cl_amc_channel_manager=>create_message_producer( i_application_id = 'Z<AMC_APP_NAME>' i_channel_id = '/demo' ). lo_producer->send( i_message = zcl_zdemo_epm_ws_dpc_ext=>gv_refresh_message ). CATCH cx_amc_error INTO DATA(lx_amc_error). MESSAGE lx_amc_error->get_text( ) TYPE 'E'. CATCH cx_apc_error INTO DATA(lx_apc_error). MESSAGE lx_apc_error->get_text( ) TYPE 'E'. ENDTRY. ENDFORM.
Then I called this procedure after I deleted the row from the DB table (I had to also add a COMMIT WORK statement before this procedure call, to ensure that the other clients don’t read the deleted row from the table).
The reverse scenario is a little tricky: we will need to trigger a PAI / PBO cycle to actually show the refreshed data and we also have to “find out” when such an update occurs. The statement WAIT FOR MESSAGING CHANNELS can be used to wait for a message to come. But if we would use this directly in a PAI / PBO module, the screen would freeze, waiting for a message to come. We want the report to be responsive, so this is out of the question.
The solution I have found is to wrap this instruction inside a aRFC call.
The function module
So, we need a function module for the aRFC. This will have a simple local AMC consumer class and just wait for a message to come. I also included an upper threshold for the amount of time that the FM waits for the messaging channels. Note that the FM should be remote enabled (to allow RFC calls).
FUNCTION ZDEMO_FM_REFRESH EXPORTING VALUE(EF_TIMEOUT) TYPE BOOLEAN. DATA: lo_receiver_text TYPE REF TO lcl_amc_test_text. ef_timeout = abap_true. CLEAR gt_message_list. TRY. DATA(lo_consumer) = cl_amc_channel_manager=>create_message_consumer( i_application_id = 'Z<AMC_APP_NAME>' i_channel_id = '/demo' ). CREATE OBJECT lo_receiver_text. lo_consumer->start_message_delivery( i_receiver = lo_receiver_text ). CATCH cx_amc_error INTO DATA(lx_amc_error). MESSAGE lx_amc_error->get_text( ) TYPE 'E'. ENDTRY. WAIT FOR MESSAGING CHANNELS UNTIL lines( gt_message_list ) >= 1 UP TO 120 SECONDS. IF lines( gt_message_list ) > 0. ef_timeout = abap_false. ENDIF. ENDFUNCTION.
Putting it together
So now that we have the FM, we can use it in the report. I simply added an aRFC call to this FM. We need a form again, to be processed when the aRFC returns its result. In order to trigger the PAI / PBO, we have to set a new ok_code using the SET USER-COMMAND instruction. I have used two different commands: one for when the FM returns because a message was received and one for when the FM returns because the maximum time limit was exceeded.
FORM return_form USING taskname. DATA lf_timeout TYPE abap_bool. RECEIVE RESULTS FROM FUNCTION 'ZDEMO_FM_REFRESH' IMPORTING ef_timeout = lf_timeout EXCEPTIONS communication_failure = 1 system_failure = 2 OTHERS = 3. IF sy-subrc = 0. IF lf_timeout = abap_false. SET USER-COMMAND 'ARFC_FUL'. ELSE. SET USER-COMMAND 'ARFC_TIM'. ENDIF. ENDIF. ENDFORM.
In the PAI I just checked the ok_code value and reloaded the table contents if the ‘AFRC_FUL’ command was given.
in both cases (‘ARFC_FUL’ and ‘ARFC_TIM’), an new aRFC call should be made. You can see the complete source code in the commit to check this out.
We can test it out now. When we delete a row on the dynpro screen, the change should automatically be propagated to the UI5 interface and vice versa.
Next week, I will make the same mechanism work between applications hosted on HCP (UI5 apps) and applications hosted on the AS ABAP (UI5 and dynpro). See you next week 🙂