
ABAP - XCO Generation und Info System
Wie kannst du mit ABAP Cloud Mitteln neue Objekte im System generieren und Informationen zu diesen erhalten? An einem Beispiel schauen wir uns das genauer an.
Inhaltsverzeichnis
Die XCO Klassen sind Hilfsklassen die verschiedene Funktionen des Alltags gebündelt unter einer öffentlichen API zur Verfügung stellen. Weitere Informationen und eine Übersicht über die XCO Bibliotheken findest du auf der Übersichtsseite.
Einleitung
Die XCO Bibliothek ist vor allem bekannt für die Fähigkeiten neue Objekte zu generieren und wird im RAP Generator und zur Generierung von Beispielen verwendet. Grundsätzlich kann dir das Framework auch viele Aufgaben durch Automatisierung abnehmen. Bei unseren IDE Actions verwenden wir die Klassen, um uns eine neue Klasse samt Interface, Factory und Injector zu generieren. Das spart einige Schritte bei der Anlage und automatisiert die Aufgabe etwas. Neben dem Generieren können wir aber auch zu bestehenden Objekten weiter Informationen erhalten.
Daher schauen wir uns in diesem Artikel einmal das Lesen von Objekten an, sowie die einzelnen Schritte zur Erzeugung einer neuen Klasse im System.
Lesen
In diesem Kapitel wollen wir eine bestehende Klasse in verschiedenen Zuständen lesen und auswerten.
Vorbereitung
Bevor wir die XCO Klassen verwenden, benötigen wir eine Klasse, die wir zum Einlesen verwenden können. In der Klasse findest du eine Konstante und zwei Methoden. Eine Methode ist statisch und öffentlich, die andere Methode ist privat. Die private Methode enthält etwas Inhalt, den wir gleich lesen möchten.
CLASS zcl_bs_demo_xco_broken DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
CONSTANTS c_empty TYPE string VALUE ``.
CLASS-METHODS static_access.
PRIVATE SECTION.
METHODS private_stuff.
ENDCLASS.
CLASS zcl_bs_demo_xco_broken IMPLEMENTATION.
METHOD private_stuff.
DATA(output) = `Hi from the method`.
ENDMETHOD.
METHOD static_access.
* if method.
* endif.
ENDMETHOD.
ENDCLASS.
Informationen
Um nun an die Informationen der Klasse zu gelangen, erzeugen wir uns ein Klassenobjekt über die XCO Klasse XCO_CP_ABAP.
DATA(class) = xco_cp_abap=>class( 'ZCL_BS_DEMO_XCO_BROKEN' ).
Das Objekt bietet uns einige Methoden, um an verschiedene Informationen zu kommen, wie zum Beispiel den aktuellen Syntax Check, den API Status, ob die Klasse existiert und weitere Informationen.
Führen wir nun einen Syntax-Check auf dem Objekt aus. Dazu rufen wir die Methode CHECK_SYNTAX auf und erhalten ein Ergebnisobjekt zurück. Da wir in unserem Beispiel in einer XCO-Classrunner sind, können wir das Objekt direkt in der Konsole ausgeben.
DATA(syntax) = class->check_syntax( ).
out->write_news( syntax ).
Möchtest du an weitere Details der Syntax kommen, stehen dir im Objekt verschiedene Attribute zur Verfügung. Über MESSAGES können wir entsprechende Fehlermeldungen erhalten und über PASSED bekommen wir den aktuellen Status der Klasse.
Möchten wir im nächsten Schritt dann den Inhalt einer Methode lesen, müssen wir eine Reihe von Schritten durchführen. Zuerst einmal müssen wir über das Attribut IMPLEMENTATION auf die aktuelle Implementierung in der Klasse zugreifen, dann lassen wir uns die Methode zurückgeben und holen uns davon die Inhalte. Zum Abschluss lesen wir noch den Quellcode aus und lassen uns eine Tabelle mit dem aktuellen Inhalt zurückgeben.
DATA(source) = class->implementation->method( 'PRIVATE_STUFF' )->content( )->get_source( ).
out->plain->write( source ).
Fehler
Nun verändern wir unsere Vorlageklasse ZCL_BS_DEMO_XCO_BROKEN und kommentieren in der Methode STATIC_ACCESS den Quellcode wieder ein. Im Anschluss speichern wir den Inhalt, damit eine aktuelle Version im Backend angelegt wird. Wir sollten nun einen entsprechenden Fehler in der Klasse haben und die Aktivierung funktioniert nicht mehr.
Führen wir nun die Klasse noch einmal aus und versuchen die beiden Schritte von oben, dann sollten wir nun über CHECK_SYNTAX die entsprechende Fehlermeldung aus der Klasse erhalten. Die zweite Methode zum Lesen des Inhalts funktioniert weiterhin. Somit haben wir einen einfachen Weg die Syntax der Klasse zu validieren und im Fehlerfall eine detaillierte Meldung zu erzeugen.
Erstellen
In diesem Kapitel ersten wir eine neue Klasse, wollen dabei ein Interface implementieren, verschiedene Methoden anlegen und einen lokalen Typen erzeugen.
Operation
Im ersten Schritt benötigen wir eine Operation, um mit der eigentlichen Anlage zu beginnen. Dazu gehen wir über die Klasse XCO_CP_GENERATION und lassen uns über das entsprechende Environment und einen Transportauftrag eine Operation anlegen. Neben PU gibt es auch noch PATCH, falls wir etwas aktualisieren wollen.
DATA(operation) = xco_cp_generation=>environment->dev_system( transport_request )->create_put_operation( ).
Spezifikation
Über die Operation erzeugen wir uns eine Spezifikation, also einen Bauplan für ein spezifisches Objekt. Daher sagen wir auf der Operation, dass wir etwas für eine Klasse machen wollen und zwar ein neues Objekt hinzufügen. Der Klasse geben wir einen Namen, weisen ein Paket zu und lassen uns die neue Spezifikation zurückgeben, um damit weiterzuarbeiten.
DATA(specification) = operation->for-clas->add_object( 'ZCL_BS_DEMO_XCO_GENERATED'
)->set_package( 'ZBS_DEMO_XCO'
)->create_form_specification( ).
Über die Spezifikation können wir dann direkt die Beschreibung am Objekt setzen.
specification->set_short_description( `Generated by ZCL_BS_DEMO_XCO_REPOSITORY` ).
Interface
Um ein Interface hinzuzufügen, gehen wir über die Definition und nutzen dazu die Methode ADD_INTERFACE, um unseren Classrunner bekannt zu geben. Um nun die Methode zu implementieren, müssen wir über die Implementierung und fügen eine neue Methode ein. Da wir die Methode aus dem Interface implementieren wollen, geben wir den Namen des Interfaces mit der entsprechenden Methode an. Im Anschluss können wir direkt den neuen Quellcode mitgeben, der in der Methode stehen soll.
specification->definition->add_interface( 'IF_OO_ADT_CLASSRUN' ).
specification->implementation->add_method( 'IF_OO_ADT_CLASSRUN~MAIN' )->set_source(
VALUE #( ( ` say_hello( name = 'Bernd' out = out ).` ) ) ).
Typ
Nun möchten wir gern einen neuen Typen in der Klasse definieren, dieser soll PRIVATE sein und ein einfacher Typ ohne Datenelement. Da Typen in der DEFINITION angelegt werden, gehen wir dann über die spezifische SECTION und ergänzen einen neuen Typ mit ADD_TYPE. Der neue Typ soll unter "NAME" zur Verfügung stehen und erhält dann über FOR unseren neuen Typen. Über TYPE und SOURCE können wir dann einen Character mit der Länge 60 definieren.
specification->definition->section-private->add_type( `name` )->for( xco_cp_abap=>type-source->for( 'c LENGTH 60' ) ).
Methode
Definieren wir nun eine neue Methode wie wir es bereits oben mit dem Interface getan haben. In diesem Abschnitt erzeugen wir die private Methode SAY_HELLO. Dieses Mal weisen wir das Objekt einer Variable zu, da wir mehrere Schritte durchführen müssen. Als nächstes fügen wir der Methode einen Importing Parameter namens NAME hinzu, geben der Methode einen DEFAULT Wert und setzen den Typen auf unseren zuvor definierten Typen. Weiterhin ergänzen wir einen zusätzlichen Importing Parameter, um das OUT Objekt aus der MAIN Methode an unsere Methode zu geben. Zum Abschluss fügen wir die neue Methode hinzu und setzen direkt auch den Quellcode.
DATA(method) = specification->definition->section-private->add_method( 'SAY_HELLO' ).
method->add_importing_parameter( 'NAME' )->set_default_value( `'Herbert'` )->set_type(
xco_cp_abap=>type-source->for( 'name' ) ).
method->add_importing_parameter( 'OUT' )->set_type( xco_cp_abap=>interface( 'IF_OO_ADT_CLASSRUN_OUT' ) ).
specification->implementation->add_method( 'SAY_HELLO' )->set_source(
VALUE #( ( ` out->write( |Hello { name }| ).` ) ) ).
Aktivierung
Nachdem unsere Klassendefinition nun abgeschlossen ist, müssen wir nur noch die Anlage ausführen. Dazu verwenden wir in der Operation die EXECUTE Methode, dann werden alle definierten Objekte angelegt. Als Default werden die Objekte zum Abschluss auch aktiviert. Möchtest du die Objekte nicht aktivieren, sondern nur im System inaktiv anlegen, dann kannst du die Option SKIP_ACTIVACTION übergeben.
* DATA(result) = operation->execute( VALUE #( ( xco_cp_generation=>put_operation_option->skip_activation ) ) ).
DATA(result) = operation->execute( ).
Ergebnis
Als Ergebnis erhalten wir im System eine neue Klasse, so wie wir sie auch definiert haben. Für die Formatierung solltest du noch einem den Pretty Printer oder am besten den ABAP Cleaner verwenden, dann musst du dich nicht mit der manuellen Formatierung des Objekts auseinandersetzen.
CLASS zcl_bs_demo_xco_generated DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
TYPES name TYPE c LENGTH 60.
METHODS say_hello
IMPORTING !name TYPE name DEFAULT 'Herbert'
!out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_xco_generated IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
say_hello( name = 'Bernd'
out = out ).
ENDMETHOD.
METHOD say_hello.
out->write( |Hello { name }| ).
ENDMETHOD.
ENDCLASS.
Die Klasse lässt sich auch direkt ausführen und wir erhalten eine Ausgabe in die Konsole.
Einschränkungen
In unserem Beispiel verwenden wir die ABAP Cloud API für die Cloud Platform (CP), diese hat einige Einschränkungen bei der Nutzung. So hast du nur Zugriff auf ABAP Cloud Objekte, also kundeneigene Objekte oder Objekte, die mit einem C1-Contract freigegeben wurden. Verwendest du eine API aus dem Bereich Classic ABAP, dann kannst du auch andere Objekte damit einlesen. Möchtest du mehr über die Unterschiede der verschiedenen APIs erfahren, dann schau bei unserem Quick Guide für XCO auf YouTube vorbei.
Komplettes Beispiel
Alle Klassen in diesem Artikel findest du als Commit bei uns im Repository auf GitHub. Damit solltest du die verschiedenen Schritte bei dir im System nachstellen können. Die generierte Klasse befindet sich ebenfalls im Repository, wenn du die Code-Generierung ausführst, solltest du die alte Klasse zuvor löschen.
Fazit
Mit der XCO Bibliothek kannst du nicht nur Objekte generieren, sondern auch Informationen zu bestehenden Objekten holen und diese gegebenenfalls anpassen. Der heutige Artikel war nur ein kleiner Einblick in das Thema Klassen und Interfaces, theoretisch gibt es für jedes ABAP Cloud Objekt die passende API.