The following texts were partially or completely generated with the help of generative AI models.
"In software engineering and computer science, a call stack (also known as procedure stack) is a specially used stack that holds the state of the subroutines currently being called during the runtime of a program." (Wikipedia)
Sometimes it is important to know the context from which a method call originated. As long as you make the method call yourself, you can of course pack this information into the appropriate parameters. But when the methods are called by the system, as is the case with BAdIs, for example, you have no influence over that. In my specific example, the goal is to distinguish whether a customer exit for a BW Bex variable is called
- During the normal execution of a report, or
- During simulation for another user (transaction RSUDO/RSECADMIN), or
- In the background during the generation of authorizations.
I cannot get the necessary information into the interface of the exit. A simple workaround to obtain this information is the function module SYSTEM_CALLSTACK. It returns the complete call stack. The following listing shows a simple test of this function:
REPORT zjb_test.
CLASS lcl_outer DEFINITION.
PUBLIC SECTION.
CLASS-METHODS test_outer.
ENDCLASS.
CLASS lcl_inner DEFINITION.
PUBLIC SECTION.
CLASS-METHODS test_inner.
ENDCLASS.
lcl_outer=>test_outer( ).
CLASS lcl_inner IMPLEMENTATION.
METHOD test_inner.
DATA lt_callstack1 TYPE abap_callstack .
CALL FUNCTION 'SYSTEM_CALLSTACK'
EXPORTING
max_level = 0
IMPORTING
callstack = lt_callstack1.
BREAK-POINT.
ENDMETHOD.
ENDCLASS.
CLASS lcl_outer IMPLEMENTATION.
METHOD test_outer.
lcl_inner=>test_inner( ).
ENDMETHOD.
ENDCLASS.
Result of the Report
Row MAINPROGRAM INCLUDE LINE BLOCKTYPE BLOCKNAME FLAG_SYSTEM<br> ============================================================================
1 ZJB_TEST ZJB_TEST 26 METHOD TEST_INNER
2 ZJB_TEST ZJB_TEST 41 METHOD TEST_OUTER
3 ZJB_TEST ZJB_TEST 18 EVENT START-OF-SELECTION
You can see that the three levels are listed in the table here.
For the actual problem, all I now need to do is search the call stack to see whether one of the classes appears in it. In my example, searching for the class CL_RS2HANA_AUTH_MANAGER, method RUN_REPLICATION would be suitable for detecting situation 3. The following call stack belongs to it:
Row MAINPROGRAM INCLUDE LINE BLOCKTYPE BLOCKNAME FLAG_SYSTEM
========================================================================================================================================================
1 ZCL_BI_CALLSTACK==============CP ZCL_BI_CALLSTACK==============CM001 5 METHOD CHECK_CONTAINS_CLASSNAME
2 ZCL_BI_VARIABLE_AUTH==========CP ZCL_BI_VARIABLE_AUTH==========CM001 9 METHOD GET_RANGE_FOR_VARNAME
3 ZCL_BI_VAR_BI_MGMTID==========CP ZCL_BI_VAR_BI_MGMTID==========CM001 10 METHOD GET_RANGE_FOR_VNAM
4 ZCL_BI_VAR_ROOT===============CP ZCL_BI_VAR_ROOT===============CM002 18 METHOD IF_RSROA_VARIABLES_EXIT_BADI~PROCESS
5 SAPLRRS0 LRRS0U01 42 FUNCTION RRS_VAR_EXIT
6 SAPLRSEC_CHECKS LRSEC_CHECKSF02 107 FORM GET_VALUE_FROM_CUST_EXIT
7 SAPLRSEC_CHECKS LRSEC_CHECKSF03 1936 FORM GET_LEAVES_AUTHORIZED
8 SAPLRSEC_CHECKS LRSEC_CHECKSF03 1782 FORM GET_VALUES_OF_LEAVES
9 SAPLRSEC_UTILS LRSEC_UTILSU02 341 FUNCTION RSEC_GET_AUTHS_FILTERED
10 CL_RS2HANA_AUTH_AUTHORIZATION=CP CL_RS2HANA_AUTH_AUTHORIZATION=CCIMP 1224 METHOD READ_AUTH_INFOPROVIDER
11 CL_RS2HANA_AUTH_AUTHORIZATION=CP CL_RS2HANA_AUTH_AUTHORIZATION=CCIMP 744 METHOD CONSTRUCTOR
12 CL_RS2HANA_AUTH_AUTHORIZATION=CP CL_RS2HANA_AUTH_AUTHORIZATION=CCIMP 1333 METHOD GET
13 CL_RS2HANA_AUTH_AUTHORIZATION=CP CL_RS2HANA_AUTH_AUTHORIZATION=CM00A 20 METHOD IF_RS2HANA_AUTH_AUTHORIZATION~IS_AUTHORIZED
14 CL_RS2HANA_AUTH_MANAGER=======CP CL_RS2HANA_AUTH_MANAGER=======CM02G 22 METHOD _STEP_01C_CHECK_AUTHORIZATIONS
15 CL_RS2HANA_AUTH_MANAGER=======CP CL_RS2HANA_AUTH_MANAGER=======CM023 20 METHOD RUN_STEP_01_PREPARE
16 CL_RS2HANA_AUTH_MANAGER=======CP CL_RS2HANA_AUTH_MANAGER=======CM02D 27 METHOD _RUN_REPLICATION
17 CL_RS2HANA_AUTH_MANAGER=======CP CL_RS2HANA_AUTH_MANAGER=======CM022 125 METHOD RUN_REPLICATION
18 RS2HANA_AUTH_RUN RS2HANA_AUTH_RUN_CL 453 METHOD RUN
19 RS2HANA_AUTH_RUN RS2HANA_AUTH_RUN 252 FORM %_SEL_SCREEN
20 RS2HANA_AUTH_RUN RS2HANA_AUTH_RUN 483 MODULE (PAI) %_END_OF_SCREEN X
21 RS2HANA_AUTH_RUN <SYSINI> 18 EVENT SYSTEM-EXIT X
For this situation I wrote a class that checks whether a combination of class and method appears in the call stack. If that is not the case, an exception is raised. The caller can then catch it and act accordingly.
CLASS zcl_callstack DEFINITION
PUBLIC FINAL CREATE PUBLIC .
PUBLIC SECTION.
CLASS-METHODS check_contains IMPORTING iv_classname TYPE seoclsname
iv_methodname TYPE seocmpname
RAISING zcx_not_found.
ENDCLASS.
CLASS zcl_callstack IMPLEMENTATION.
METHOD check_contains.
DATA lt_callstack TYPE abap_callstack .
DATA lv_mainprogram_pattern TYPE progname VALUE '==============================CP'.
DATA(lv_classlength) = strlen( iv_classname ).
lv_mainprogram_pattern(lv_classlength) = iv_classname.
CALL FUNCTION 'SYSTEM_CALLSTACK'
EXPORTING
max_level = 0
IMPORTING
et_callstack = lt_callstack.
READ TABLE lt_callstack TRANSPORTING NO FIELDS
WITH KEY mainprogram = lv_mainprogram_pattern
blockname = iv_methodname.
IF sy-subrc NE 0.
RAISE EXCEPTION TYPE zcx_not_found.
ENDIF.
BREAK-POINT.
ENDMETHOD.
ENDCLASS.
The approach shown works well. However, it should only be used when passing the information by other means, e.g. via a parameter, is not possible. The reason for this is that a specific call sequence in the call stack does not represent a defined interface. Internally, SAP can change everything, as long as the exit is called with the same parameters.



