ABAP Callstack auswerten - Wer ruft mich an?
Veröffentlicht am 9. Dezember 2019 von | ABAP | News |
"Unter einem Aufrufstapel (englischcall stack, procedure stack) versteht man in der Softwaretechnik und Informatik einen besonders genutzten Stapelspeicher, der zur Laufzeit eines Programms den Zustand der gerade aufgerufenen Unterprogramme enthält." (Wikipedia)
Manchmal ist es wichtig zu wissen, aus welchem Kontext ein Methodenaufruf kam. Solange man selber den Methodenaufruf macht, kann man diese Information natürlich in entsprechende Parameter packen. Aber wenn es vom System aufgerufene Methoden sind, wie z.B. bei BAdIs, dann hat man darauf keinen Einfluss. In meinem konkreten Beispiel geht es darum zu unterscheiden, ob ein Customer Exit für eine BW Bex Variable entweder
- Beim normalen Ausführen eines Reports oder
- Beim Simulieren für einen anderen Benutzer (Transaktion RSUDO/RSECADMIN)
- Im Hintergrund bei der Generierung der Berechtigungen
aufgerufen wird. In die Schnittstelle des Exits bekomme ich die notwendige Information nicht mit rein. Ein einfacher Workaround, um sich diese Information zu beschaffen, ist der Funktionsbaustein SYSTEM_CALLSTACK. Er liefert den vollständigen Callstack. Das folgende Listing zeigt einen einfachen Test dieser Funktion:
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.
Ergebnis des Reports
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
Sie sehen, dass hier die drei Ebenen in der Tabelle stehen.
Für das eigentliche Problem muss ich jetzt lediglich den Callstack durchsuchen, ob dort eine der Klassen vorkommt. In meinem Beispiel wäre z.B. die Suche nach der Klasse CL_RS2HANA_AUTH_MANAGER, Methode RUN_REPLICATION für die Erkennung der Situation 3 geeignet. Dazu gehört der folgende Callstack:
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
Für diese Situation habe ich eine Klasse geschrieben, die prüft, ob im Callstack eine Kombination aus Klasse und Methode vorkommt. Falls das nicht der Fall ist, wird eine Exception ausgelöst. Diese kann der Aufrufer dann abfangen und entsprechend handeln.
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.
Das gezeigte Vorgehen funktioniert gut. Es sollte aber nur dann eingesetzt werden, wenn eine Weitergabe der Information auf andere Art, z.B. per Parameter, nicht möglich ist. Der Grund hierfür ist, das eine bestimmte Aufrufreihenfolge im Callstack keine definierte Schnittstelle darstellt. Intern kann die SAP alles ändern, solange der Exit mit den gleichen Parametern aufgerufen wird.