ABAP - CL_BP_ABAP_JOB
In diesem Artikel geht es um die Einplanung von Jobs mit Hilfe der Klasse CL_BP_ABAP_JOB und wieso ihr besser die Klasse als die Funktionsbausteine verwendet.
Inhaltsverzeichnis
Woran denkst du als Erstes, wenn du an die Einplanung von Jobs denkst? Wahrscheinlich erst einmal an die Transaktion SM36/7 oder die Funktionsbausteine JOB_CREATE und JOB_SUBMIT? Wir zeigen dir heute die Klasse CL_BP_ABAP_JOB und wie du damit ganz leicht eigene Jobs einplanen kannst.
Funktionsbausteine
Die ersten klassischen Objekte, die Funktionsbausteine, waren am Anfang vor allem wegen der gekapselten und wiederverwendebaren Logik sehr beliebt. Im Laufe der Zeit stellten sich aber auch Nachteile ein, da sie im Gegensatz zu Klasse relativ starr sind. Möchte man Fehler nach außen geben, muss man zahlreiche Exceptions definieren, mit denen eine Rückgabe von zusätzlichen Informationen relativ schwer war. Die Schnittstelle und Implementierung im Quellcode namen daher in manchen Fällen schon einmal 30-50 Zeilen ein und das nur um eine Funktion aufzurufen, was den Quellcode nicht unbedingt übersichtlicher machte. Dazu kamen Nachteile des statischen Aufbaus und das globale Daten nach einem Aufruf immer noch verfügbar waren.
SAP hat deshalb in vielen Bereichen auch angefangen, Funktionsbausteine in Klassen zu packen (Facade Pattern) und eine (relativ) saubere Schnittstelle zur Verfügung zu stellen. Damit sollten leichtere öffentlich wiederverwendbare APIs geschaffen werden, was in vielen Fällen auch ganz gut geklappt hat.
CL_BP_ABAP_JOB
So eine Klasse ist auch CL_BP_ABAP_JOB die die verschiedenen Funktionsbausteine aus dem Bereich der Jobeinplanung kapselt. Wir können dir aber jetzt schon sagen, dass die Klasse nicht zu 100% sauber entwickelt wurde, kann aber für einfache Einplanungen von dir genutzt werden.
DATA(lo_job) = NEW cl_bp_abap_job( ).
lo_job->set_name( 'CLASS_JOB_START' ).
lo_job->set_report( 'Z_TEST_REPORT' ).
lo_job->set_variant( 'TEST' ).
lo_job->set_effective_user( sy-uname ).
lo_job->set_language( sy-langu ).
lo_job->start_simple( ).
Hierbei handelt es sich um die einfachste Form der Einplanung eines Jobs. Die Klasse benötigt im ersten Teil erst einmal die Informationen zum Jobnamen, Report und der Variante die ausgeführt werden soll. Als zweiten Schritt wird der ausführende User benötigt und die Sprache in der der Job eingeplant werden soll. Im Anschluss wird der Job mit der Methode START_SIMPLE sofort eingeplant und ausgeführt.
Hinweis: Die Methode START_SIMPLE bietet keine Möglichkeit eines Fehlerhandlings, sind nicht alle Parameter befüllt, bekommst du als Aufrufer keinen Hinweis darüber. Der Job eingeplant, wenn soweit alles in Ordnung ist. Für einfache Einplanungen reicht diese Methode, für ein genaueres Fehlerhandling, musst du allerdings auf unsere zweite Variante wechseln.
Bessere Einplanung
Eine bessere und genauere Einplanung schaffst du mit der gleichen Klasse über das Interface IF_BP_JOB_ENGINE, da hier granularere Möglichkeiten geschaffen werden. Leider sind die Methoden noch mit dem alten Exception Handling ausgestattet, was die Handhabung nicht ganz so "leicht" macht. Der Anfang ist so ähnlich wie bei der einfachen Einplanung, wobei du noch zusätzlich den Jobtypen setzen musst, das dieser benötigt wird (Methode SET_JOB_TYPE).
DATA(lo_job) = NEW cl_bp_abap_job( ).
lo_job->set_name( 'CLASS_JOB_START' ).
lo_job->set_job_type( cl_bp_const=>job_type_abap ).
lo_job->set_report( 'Z_TEST_REPORT' ).
lo_job->set_variant( 'TEST' ).
lo_job->set_effective_user( sy-uname ).
lo_job->set_language( sy-langu ).
lo_job->start_simple( ).
Als nächsten Schritt castest du die Klasse auf das Interface IF_BP_JOB_ENGINE oder sprichst das Interface direkt über die Klasse an. Diesen Schritt machen wir für einen leichteren Zugriff auf die Methoden. Der Job benötigt noch einen Auslöser (Trigger), diesen setzen wir mit der Methode SET_TRG_TYPE und verwenden dabei den Trigger für den Start per Datum und Uhrzeit. Der Trigger wird im nächsten Schritt vorbereitet und am Ende an die Engine übergeben.
DATA(lo_engine) = CAST if_bp_job_engine( lo_job ).
lo_job->set_trg_type( cl_bp_const=>trg_type_date ).
DATA(lo_trigger) = NEW cl_tc_date_trigger( ).
lo_trigger->set_sdlstrtdt( sy-datum ).
lo_trigger->set_sdlstrttm( CONV t( sy-uzeit + 30 ) ).
lo_engine->generate_job_count(
EXCEPTIONS
cant_create_job = 1
invalid_job_data = 2
jobname_missing = 3
OTHERS = 4
).
lo_engine->plan_job_step(
EXCEPTIONS
bad_priparams = 1
bad_xpgflags = 2
invalid_jobdata = 3
jobname_missing = 4
job_notex = 5
job_submit_failed = 6
program_missing = 7
prog_abap_and_extpg_set = 8
OTHERS = 9
).
lo_engine->release_job(
EXPORTING
i_trg_obj = lo_trigger
EXCEPTIONS
cant_start_immediate = 1
invalid_startdate = 2
jobname_missing = 3
job_close_failed = 4
job_nosteps = 5
job_notex = 6
lock_failed = 7
OTHERS = 8
).
Bei diesem Schritt planen wir den Job für in 30 Sekunden ein. Neben dem Trigger CL_TC_DATE_TRIGGER, gibt es auch die Klasse CL_TC_EVT_TRIGGER, um einen Job einzuplanen, der auf ein Event reagiert. Wie du siehst, ist hier das klassische Exception Handling im Einsatz, welches nicht ganz so hübsch zu handeln ist.
Austauschbar
Solche API Komponenten, wie diese Klasse, sollen im Grunde auch dafür sorgen, dass man ohne viele Aufwand auch einmal die dahinterliegende Infrastruktur austauschen kann, ohne viel Quellcode dabei zu ändern. Solche Konstrukte sind prima für Migrationen geeignet ohne all zu viel Quellcode austauschen zu müssen. Ein beliebtes Pattern für solche Objekte sind auch DAOs (Data Access Objects).
Fazit
Mit der heute vorgestellten Klasse kannst du einfache Jobeinplanungen leicht durchführen und erhältst dafür eine leichte Schnittstelle mit kleineren Fehlern. Für schnelle Implementierung geeignet und um deinen eigenen Code schmall zu halten.