ABAP Tipp - RFC Puffer
Hier ein kleiner Tipp von uns, was beim Lesen über RFC mit dem Puffer so alles schief gehen kann und auf was du dabei unbedingt achten solltest.
Inhaltsverzeichnis
Hierbei geht es um ein Remoteszenario, das Daten aus einem anderen System liest und diese zur Verfügung stellt. Der Aufruf erfolgt in einer Schleife, sodass der RFC Funktionsbaustein mehrmals aufgerufen wird. Dadurch kommt es zu einem spannenden Ergebnis.
Aufbau
Für den initialen Aufbau des Szenarios stellen wir eine Klasse im ersten System zur Verfügung, die den Funktionsbaustein für uns aufrufen soll. Der Einfachheit verwenden wir eine kleine Schleife und übergeben den Index an den Baustein, dafür erwarten wir den entsprechenden Buchstaben aus dem Alphabet.
CLASS zcl_test_rfc_call DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_test_rfc_call IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
lt_letters TYPE string_table.
DO 26 TIMES.
DATA(ld_index) = sy-index - 1.
CALL FUNCTION 'Z_RFC_BUFFER' DESTINATION 'DUMMY'
EXPORTING
id_index = ld_index
IMPORTING
et_letters = lt_letters.
out->write( concat_lines_of( lt_letters ) ).
ENDDO.
ENDMETHOD.
ENDCLASS.
Auf der anderen Seite (System) wird entsprechend der Funktionsbaustein implementiert und als RFC Funktionsbaustein gekennzeichnet, damit du darauf zugreifen kannst.
FUNCTION Z_RFC_BUFFER
IMPORTING
VALUE(ID_INDEX) TYPE SYINDEX
EXPORTING
VALUE(ET_LETTERS) TYPE STRING_TABLE.
et_letters = lcl_function=>get_letter( id_index ).
ENDFUNCTION.
Hier verwenden wir nach einer sauberen Architektur eine Klasse, allerdings kein Objekt, sondern eine statische Klasse, die die Daten enthält.
CLASS lcl_function DEFINITION FINAL.
PUBLIC SECTION.
CLASS-DATA:
gt_letters TYPE string_table.
CLASS-METHODS:
get_letter
IMPORTING
id_index TYPE syindex
RETURNING VALUE(rt_letters) TYPE string_table.
PRIVATE SECTION.
CLASS-METHODS:
find_letter
IMPORTING
id_index TYPE syindex.
ENDCLASS.
CLASS lcl_function IMPLEMENTATION.
METHOD get_letter.
find_letter( id_index ).
rt_letters = gt_letters.
ENDMETHOD.
METHOD find_letter.
INSERT CONV string( substring( val = sy-abcde off = id_index len = 1 ) ) INTO TABLE gt_letters.
ENDMETHOD.
ENDCLASS.
Erwartung
Beim Aufruf des Funktionsbausteins werden im Normalfall auch die Daten im globalen Bereich gehalten, solange die Anwendersession noch läuft. Nach dem Beenden der Session werden die Daten gelöscht und eine Funktionsgruppe auf Null gesetzt. Im Fall eines RFC Funktionsbausteins, gehen wir also davon aus, dass nach jedem Aufruf der Speicher des Funktionsbausteins gelöscht wird, da dann die Session im Zielsystem abgeschlossen ist.
Im Ergebnis sollte also in der Console eine Liste aller Buchstaben nacheinander ausgegeben werden, da mit jedem Aufruf ein Buchstabe abgerufen wird.
Ergebnis
Das Ergebnis der Schleife sieht nun also wie folgt aus und entspricht damit nicht dem erwarteten Ergebnis. Die bisherig ermittelten Buchstaben werden ebenfalls zurückgegeben und die Tabelle immer nur um den aktuellsten Buchstaben erweitert.
Wieso weicht das Ergebnis von unserer Erwartung ab? Nach dem Aufruf des Funktionsbausteins wird die Session auf dem Zielsystem nicht abgebaut und bleibt so lange existent, wie auch unsere Session läuft. Würde man zwischen zwei Systemen erst einmal nicht erwarten, ist aber so gewollt, wenn man einmal große Datenmengen abholen möchte und dann mit Datenbankzeigern arbeitet.
Wie du siehst ist die Arbeit mit globalen Variablen und statischen Attributen außerhalb von Puffereffekten sehr riskant. Wir raten dir daher immer auf Objekte und Instanzen zu setzen, um so nach einem Aufruf sicher zu sein, dass die Instanz gelöscht wird und der Speicher komplett bereinigt wurde.
Dieses kleine Beispiel soll demonstrieren wie es auch in komplexen Klassen aussehen kann, wenn diese nicht sauber verwendet werden. Wir betrachten dabei erst einmal nicht die Verwendung von globalen Variablen, hier besteht aber das gleiche Risiko.
Fazit
Beim Design von RFC Funktionsbausteinen solltest du immer darauf achten, was nach einem Aufruf mit deinen Daten im Zielsystem passiert und ob du dieses Verhalten wirklich haben möchtest. Du solltest nicht davon ausgehen das die Daten nach dem Aufruf aufgeräumt werden und wenn du zu viel erhältst, könnte es am Puffer auf der anderen Seite liegen.