BTP - Anbindung On-Premise (Funktionsbaustein)
Hier schauen wir uns einmal die Anbindung eines Funktionsbausteins im ABAP Environment an und wie du diesen aufrufen kannst.
Inhaltsverzeichnis
Bereits in einem anderen Artikel haben wir On-Premise Daten per OData aus dem Backend System abgefragt. In diesem Artikel werden wir uns einmal das Konstrukt Funktionsbaustein anschauen und für wen dieses Szenario relevant ist.
Einleitung
Als das ABAP Environment von SAP freigegeben wurde, gab es viele Dinge noch nicht und der Funktionsumfang war noch sehr rudimentär. Dazu zählten auch die Zugriffsmethoden auf das Backend. Zugriffe auf RFC-Funktionsbausteine wurden erst viel später implementiert, um den Kunden die Möglichkeit zu geben, bestehende Szenarien auf der BTP weiter nutzen zu können.
Hinweis: Daher sollte diese Technologie mit Vorsicht verwendet werden, da sie nur einen Übergang zu OData erkaufen soll. Gibt es also CDS Views oder OData die du nutzen kannst, dann solltest du lieber dies tun.
Aufbau On-Premise
Dabei setzen wir wieder auf den gleichen Daten auf, die wir auch im OData Artikel genutzt haben. Dazu erstellen wir einen Funktionsbaustein und implementieren eine rudimentäre Zugriffslogik auf die Daten. Es gibt zwei Importparameter für den Namen und die Branche. Wir geben dann die Einträge zurück und einen möglichen Fehlertext.
FUNCTION z_bs_demo_get_cnames
IMPORTING
VALUE(id_name) TYPE zbs_dmo_cname-name
VALUE(id_branch) TYPE zbs_dmo_cname-branch
EXPORTING
VALUE(et_cnames) TYPE zbs_t_demo_cnames
VALUE(ed_error) TYPE string.
DATA:
lt_r_name TYPE RANGE OF zbs_dmo_cname-name,
lt_r_branch TYPE RANGE OF zbs_dmo_cname-branch.
TRY.
IF id_name IS NOT INITIAL.
INSERT VALUE #( sign = 'I' option = 'CP' low = id_name ) INTO TABLE lt_r_name.
ENDIF.
IF id_branch IS NOT INITIAL.
INSERT VALUE #( sign = 'I' option = 'CP' low = id_name ) INTO TABLE lt_r_branch.
ENDIF.
SELECT FROM zbs_dmo_cname
FIELDS *
WHERE name IN @lt_r_name
AND branch IN @lt_r_branch
INTO TABLE @et_cnames.
CATCH cx_root INTO DATA(lo_error).
ed_error = lo_error->get_text( ).
ENDTRY.
ENDFUNCTION.
Metadaten
Als Nächstes benötigen wir ebenfalls die Metadaten des Funktionsbausteins, doch hier kommt keine URL zum Einsatz, da es keinen öffentlichen Endpunkt gibt. Dazu rufen wir auf dem Backend System die Transaktion ACO_PROXY auf und führen die folgenden Einstellungen durch:
Im unteren Bereich muss die Transaktion auf Datei (File) stehen, damit eine Metadaten Beschreibung erzeugt wird. Beim Ausführen des Reports wird es dann zur Abfrage des Dateinamens und -standort kommen. Mit diesem Schritt haben wir die Metadaten des Funktionsbausteins extrahiert.
Consumption Model
Nun können wir im ABAP Environment das Consumption Model für den Funktionsbaustein anlegen, dazu wählen wir auf dem Paket ZBS_DEMO_RAP_INTERFACE über das Kontext-Menü (Recht-Klick) "New -> Other Repository Object" und suchen das "Service Consumption Model". Dann gelangen wir in den Auswahldialog und befüllen die Felder mit Informationen, wobei der Mode auf RFC stehen sollte:
Im nächsten Schritt geben wir die Metadaten Beschreibung mit (Pfad der Datei) und bestätigen im nächsten Schritt noch den Transport:
Es werden nun entsprechende Objekte generiert, darunter das Consumption Model und die Proxy Klasse. Die Proxy Klasse wird uns später für den Zugriff zur Verfügung stehen:
Lesen
Nun wollen wir einmal die Datensätze lesen und implementieren dazu etwas Beispiel-Coding. Dazu erzeugen wir über die Destination eine separate Verbindung für den RFC Zugriff und übergeben diese an den Konstruktor der Proxy Klasse. Den Funktionsbaustein können wir als Methode der Klasse aufrufen, die Typen sind bereits in der Klasse definiert:
DATA(lo_destination) = cl_rfc_destination_provider=>create_by_cloud_destination( c_destination ).
NEW zbs_demo_rap_onprem_func( lo_destination )->z_bs_demo_get_cnames(
EXPORTING
id_branch = ''
id_name = ''
IMPORTING
ed_error = DATA(ld_error)
et_cnames = DATA(lt_company_names)
).
Wir übergeben dabei keine Einschränkungen und lassen uns die Ergebnisse zurückgeben. Diese geben wir nun in die Console aus und erhalten das folgende Ergebnis:
Die vollständige Implementierung in der Testklasse sieht wie folgt aus:
CLASS zcl_bs_demo_read_func DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
CONSTANTS:
c_destination TYPE string VALUE `<destination-service-id>`.
ENDCLASS.
CLASS zcl_bs_demo_read_func IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
TRY.
DATA(lo_destination) = cl_rfc_destination_provider=>create_by_cloud_destination( c_destination ).
NEW zbs_demo_rap_onprem_func( lo_destination )->z_bs_demo_get_cnames(
EXPORTING
id_branch = ''
id_name = ''
IMPORTING
ed_error = DATA(ld_error)
et_cnames = DATA(lt_company_names)
).
out->write( 'Error from backend:' ).
out->write( ld_error ).
out->write( 'Companies:' ).
out->write( lt_company_names ).
CATCH cx_root INTO DATA(lo_error).
out->write( lo_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.
Fazit
Der Aufruf eines RFC-Funktionsbausteins ist an einigen Stellen viel übersichtlicher und einfacher als der OData Service, sollte allerdings auch nicht dazu verleiten, nur Funktionsbausteine zu verwenden. Für den Übergang Richtung OData ist es eine leichte Alternative.