Example implementation of a custom error message provider in App Support for the SAP Fiori Launchpad

Following the business process described in my previous post, this article explains an example implementation of a custom message provider for App Support. App Support uses BAdI Implementations to retrieve error messages and logs from a Front-End or Back-End server. This enables customers to create their own log projects outside of the delivered standard solution.

Class Implementation

When the BAdI implementation and the empty class have been created for enhancement definition /UI2/ADE_DATA_PROVIDER, we can continue in SE80 or the ABAP Development Tools. For ease of use, we can use a superclass to reuse some of the standard operations, which are typically used for this type of implementation. The superclass is /UI2/CL_ADE_BADI_DEFAULT:


Assignment of super class /UI2/CL_ADE_BADI_DEFAULT in transaction SE80

After adding the superclass, we need to delete the following interfaces, which have been added automatically during to the BAdI implementation, as they have already been implemented in the superclass:


After removing the interfaces from our inheriting class in tab Interfaces, it should be possible to activate. Please note there won’t be any changes on the UI just yet.

Since we’ll be implementing a remote source (data is retrieved from the Back-End Server), we additionally have to implement interface /UI2/IF_ADE_BADI_CALLBACK_SRC.


Interfaces required for the BAdI implementation

Now we’re all set and are able to redefine the methods accordingly.


Inherited methods of the super class can be redefined

We best start with the constructor method, where we define the source as a remote source (data coming from Back-End Server):

 method CONSTRUCTOR. "-! register as remote source here super->constructor( iv_is_remote = abap_true ). endmethod.

The next and very important method to redefine is VERIFY_SOURCE_AVAILABLE. Without this implementation, App Support won’t bother to follow up on the source. This implementation tells the framework, that the implementation is relevant for the current App context. Other restrictions, such as client-dependency can be added here as well.

 METHOD /ui2/if_ade_badi_data_provider~verify_source_available.
*** In this method App Support decides, if the BAdi implementation is relevant for the current application
*** to retrieve the correct appkey, simply debug the first time here, when in the application.
***--------------------------------------------------------------------------------------------------------- ev_available = /ui2/if_ade_badi_data_provider~sc_status_not_available. "Check if App = Display Outbound delivery (Type: WebGUI, Tx: VL02n) IF is_keys-appkey <> 'X-SAP-UI2-PAGE:X-SAP-UI2-CATALOGPAGE:SAP_LE_BC_OD_PROC:00O2TOBXDLX6WIZLS0DUK2RL0'. RETURN. ENDIF. "now some more specific check, if the BAdI should be called here. In this scenario a log from a remote server "is retrieved. Therefore we wait for a adeRemoteServer entry. If the log is from a local source, simply wait for "adeLocalServer IF is_keys-id_lvl1 <> 'adeLocalServer'. "-! Important - this will make the entry available in the UI - after method GET_DISPLAY_NAME has been implemented ev_available = /ui2/if_ade_badi_data_provider~sc_status_available. "-! Important - set keys for remote proxy processing in CALLBACK_REMOTE_SOURCE and message handling ms_keys = is_keys. "-! register at App Support proxy hub, as this BAdI implements the remote interface, additionally set is remote in CONSTRUCTOR me->register_at_proxy( ). ENDIF. ENDMETHOD.

In Method GET_DISPLAY_NAME we can now define how the new menu entry should look like. Additionally, we’ll provide a link to another representation of these messages, in our case SMQ2.

 METHOD /ui2/if_ade_badi_data_provider~get_display_name.
*** In this method App Support retrieves the name of the menu entry and a link to an original log
*** transaction, for example a general link to SLG1.
***--------------------------------------------------------------------------------------------------------- "This is the name displayed in the menu, ideally the text is translated into different languages es_display_name-name = 'Deliveries Inbound Queue'. "this method creates a WebGUI link for the entered transaction ev_link_to_log = /ui2/cl_ade_configuration=>get_instance( )->create_webgui_link( EXPORTING iv_transaction = 'SMQ2' iv_rfc_destination = iv_dest ). ENDMETHOD.

With this implementation we should already see the new menu entry in the application “Display Outbound Delivery”:


The menu entry is already visible, but no error messages yet

Currently no hanging queues are displayed, but we’ll do this in the next step. The link we added is available when clicking this icon:


Please note: In case the menu entry is not displayed, please check your authorizations for object S_FLP_AS. Our new filter value should now be available as data source for field SUI_ADEDS in the selection criteria. If it is not yet set in your profile, add it to your authorizations to display the new menu entry.


Available values in authorization setup in PFCG on the Front-End Server

For further details, on the authorization setup please check the documentation.

Now we only have to take care of retrieving the actual queue entries. For this example implementation, I’ll use function module TRFC_GET_QIN_INFO_DETAILS, which is RFC enabled, but not released for customers.

The function module retrieves all inbound queues of the called system, depending on the queue name (pretty much same as SMQ2). Therefore, it is a good fit for our example here – the only catch is that we should use it in an asynchronous manner, since we don’t want to cause any negative impact on App Support’s performance. So, let’s look at the implementation of method GET_SOURCE_INFORMATION:

 METHOD /ui2/if_ade_badi_data_provider~get_source_information.
*** The usage of this method depends on the type of Source, which is being used. In case the
*** messages are retrieved from a local source, the source (db table, local fm, etc.) can be
*** collected here directly and passed to ET_MESSAGES.
*** For a remote source it is recommended to use the asynchronuous processing, via usage of
*** STARTING NEW TASK. This way the application remains stable, even if the remote source
*** triggers a timeout. In case of a remote source the messages are retrieved in method
*** CALLBACK_REMOTE_SOURCE and do not have to be provided here.
***--------------------------------------------------------------------------------------------- DATA: lv_task(32) TYPE c, lv_qname TYPE trfcqnam, lv_message TYPE c LENGTH 250. IF mv_mandt IS INITIAL. " set client and system id to add them in CALLBACK_REMOTE_SOURCE /ui2/cl_ade_configuration=>get_instance( )->get_system_client_by_rfc_dest( EXPORTING iv_rfc_dest = iv_rfc_dest " Logical RFC Destination - Points to SAP system IMPORTING ev_sysid = mv_sysid " Target system ev_client = mv_mandt "target client ). "later in the callback function we can't call this method, as we run in background -> therefore create template link for log queues mv_template_link = /ui2/cl_ade_configuration=>get_instance( )->create_webgui_link( EXPORTING iv_transaction = 'SMQ2' it_query_parms = VALUE #( ( name = 'QNAME' value = 'MYPLACEHOLDER' ) ) "this is the parameter in SMQ2 -> MYPLACEHOLDER will be removed later iv_rfc_destination = iv_rfc_dest ). ENDIF. "This entry depends on how the RFC queues are setup in the remote system. Here we're reading inbound queues from the same system "and client, as EWM is on the same system. lv_qname = 'DLWS' && mv_sysid && 'CLNT' && mv_mandt && '*'. "To call the function module in a separate task, we need to provide a task id. Ideally related to our BAdI lv_task = is_keys-id. "Here the remote RFC function module is called in a new task. As a callback the interface method callback_remote_source "is used, which has to be implemented by this class (Implement interface /UI2/IF_ADE_BADI_CALLBACK_SRC) CALL FUNCTION 'TRFC_GET_QIN_INFO_DETAILS' STARTING NEW TASK lv_task "maximum length 32 DESTINATION iv_rfc_dest CALLING /ui2/if_ade_badi_callback_src~callback_remote_source ON END OF TASK EXPORTING qname = lv_qname client = mv_mandt EXCEPTIONS communication_failure = 1 MESSAGE lv_message system_failure = 2 MESSAGE lv_message OTHERS = 3. IF sy-subrc <> 0. /ui2/cx_ade=>raise_error_in_fm( EXPORTING iv_fm_name = 'TRFC_GET_QIN_INFO_DETAILS' iv_error = CONV #( lv_message ) ). ENDIF. ENDMETHOD.

The method can be split into two parts. The first part only stores some information in the object instance, based on the current context. The second part calls the function module and sets method CALLBACK_REMOTE_SOURCE as callback interface, when the data is retrieved. For an embedded system implementation, the function module could be used without the STARTING NEW TASK statement. In this case we could also pass the messages to ET_MESSAGES directly, but as we are in a distributed landscape, we’ll implement it with an remote enabled function call.

The target RFC connection of the currently displayed app is provided as input parameter, so we know the destination for our function call right away. The actual results of the remote function call are processed in method CALLBACK_REMOTE_SOURCE, when the data has been retrieved from the Backend Server:

 METHOD /ui2/if_ade_badi_callback_src~callback_remote_source.
*** This method is called, when the RFC retrieves a result. With the statement RECEIVE RESULTS FROM FUNCTION
*** the actual exporting parameters are obtained from the FM. The messages should then be mapped to the
*** standard message format of /ui2/ade_s_diagnostics_log. In the last step the messages are passed to the
*** proxy instance. The proxy instance will take care of delivering them to App Support.
***--------------------------------------------------------------------------------------------------------- DATA: lt_rfcqueue TYPE STANDARD TABLE OF trfcqin, lv_message(250) TYPE c. "Get messages from callback RECEIVE RESULTS FROM FUNCTION 'TRFC_GET_QIN_INFO_DETAILS' TABLES qtable = lt_rfcqueue EXCEPTIONS system_failure = 1 MESSAGE lv_message communication_failure = 2 MESSAGE lv_message OTHERS = 3. "map messages to target format LOOP AT lt_rfcqueue ASSIGNING FIELD-SYMBOL(<ls_entry>). "we are only interested in queues in status failed IF <ls_entry>-qstate <> 'SYSFAIL'. CONTINUE. ENDIF. "build a message line for each sysfail SMQ2 entry APPEND VALUE #( s_keys = ms_keys username = ms_keys-username source = /ui2/if_ade_badi_data_provider=>sc_source_remote type = 'E' msg_date = <ls_entry>-qrfcdatum msg_time = <ls_entry>-qrfcuzeit error_text = <ls_entry>-errmess context = substring( val = <ls_entry>-qname off = 14 ) "add delivery number as context information (last part of queue name) sysid = mv_sysid "determined in GET_SOURCE_INFORMATION client = mv_mandt "determined in GET_SOURCE_INFORMATION "The template link has been set GET_SOURCE_INFORMATION for easier use here. Read more will now lead to the SMQ2 log entry. more_info_link = replace( val = mv_template_link sub = 'MYPLACEHOLDER' with = <ls_entry>-qname ) ) TO mt_messages. ENDLOOP. "set in result list in the proxy instance, which will dispatch it to the UI me->mo_proxy_instance->callback_remote_data_source( iv_id = ms_keys-id it_messages = mt_messages ). ENDMETHOD.


With relatively little implementation effort, it is possible to collect logs and error messages in a central location in the SAP Fiori Launchpad. App Support allows flexible enhancements for customer specific projects using the widely accepted BAdI concept.

For Questions and Answers on SAP Fiori – please see the SAP Community Q&A area and feel free to post your own questions.

I hope you found this blog post helpful – feel free to leave comments and feedback, you can follow the SAP Fiori Launchpad tag to receive updates on blog posts here.