Periodischer Job mit unabhängigen Steps

Jobverarbeitung is in vielen Bereichen und für viele Funktionalitäten wichtig. Jobeinplanungen sind eine gängige Technik, die jahrzehntelang erprobt ist. Kürzlich bin ich in einem Projekt auf eine Anforderung gestoßen, die nicht so ohne Weiteres mit den Standardmitteln der Jobeinplanung möglich war.

Anforderung

Es soll ein periodischer Job mit mehreren Steps eingeplant werden, deren Verarbeitungsschritte (Steps) unabhängig voneinander sind. Unabhängig heißt in diesem Fall, dass ein Verarbeitungsschritt, der durch einen Kurzdump abbricht, die Ausführung der anderen Schritte nicht beeinträchtigen soll.

Möglichkeit 1

Die erste auf der Hand liegende Möglichkeit ist, einen Job periodisch einzuplanen, der einzelne Verarbeitungsschritte enthält. Leider bricht der gesamte Job ab, wenn einer der Verarbeitungsschritte durch einen Shortdump abbricht. Diese Möglichkeit kommt also nicht in Betracht.

Möglichkeit 2

Die zweite Möglichkeit wäre, die einzelnen Verarbeitungsschritte als einzelne Jobs zu definieren und jeweils den einen Job als Vorgänger des jeweils nächsten Jobs zu definieren. Aber auch hier gibt es eine Einschränkung: Diese Jobs können nicht periodisch geplant werden.

Lösung

Die Lösung für mich war in diesem Fall natürlich ein ABAP Programm. Das folgende Programm kann man periodisch als Job einplanen und hier bis zu fünf Programme unabhängig voneinander, jeweils mit Vorgänger – Nachfolger-Beziehung, definieren. Wenn das Programm ausgeführt wird, startet es das erste Programm sofort als Job. Das zweite Programm wird gestartet, wenn der Job für das erste Programm beendet wurde und so weiter. Dabei ist es unerheblich, ob der Vorgängerjob regulär beendet wurde oder abgebrochen ist.

Das Programm ist fest auf fünf Verarbeitungsschritte ausgelegt. Es kann einfach auf weitere Steps erweitert werden. Sofern noch sehr viele Verarbeitungsschritte verwaltet werden sollten, sollte man die Verarbeitung dynamisch programmieren. Dafür müsste man jedoch auch die Eingabe für die Definition der Verarbeitungsschritte so anpassen, dass die einzelnen Programme in einer Liste (Grid) eingegeben werden können.

Report mit beispielhaften Reporteinplanungen

Coding

REPORT z_schedule_05_jobs_periodic.

PARAMETERS nam1 TYPE btcjob     OBLIGATORY.
PARAMETERS rep1 TYPE syrepid    OBLIGATORY.
PARAMETERS var1 TYPE raldb_vari OBLIGATORY.
SELECTION-SCREEN SKIP 1.

PARAMETERS nam2 TYPE btcjob.
PARAMETERS rep2 TYPE syrepid.
PARAMETERS var2 TYPE raldb_vari.
SELECTION-SCREEN SKIP 1.

PARAMETERS nam3 TYPE btcjob.
PARAMETERS rep3 TYPE syrepid.
PARAMETERS var3 TYPE raldb_vari.
SELECTION-SCREEN SKIP 1.

PARAMETERS nam4 TYPE btcjob.
PARAMETERS rep4 TYPE syrepid.
PARAMETERS var4 TYPE raldb_vari.
SELECTION-SCREEN SKIP 1.

PARAMETERS nam5 TYPE btcjob.
PARAMETERS rep5 TYPE syrepid.
PARAMETERS var5 TYPE raldb_vari.


CLASS lcx_job DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.

CLASS lcl_define_job DEFINITION.
  PUBLIC SECTION.
    METHODS constructor
      IMPORTING
        iv_name    TYPE btcjob
        iv_report  TYPE syrepid
        iv_variant TYPE raldb_vari
      RAISING
        lcx_job.

    METHODS start
      IMPORTING
        iv_pred_jobcount TYPE btcjobcnt OPTIONAL
        iv_pred_jobname  TYPE btcjob OPTIONAL
      RAISING
        lcx_job.

    DATA mv_jobcount     TYPE btcjobcnt.
    DATA mv_jobname      TYPE btcjob.
    DATA mv_released     TYPE abap_bool.

ENDCLASS.

CLASS lcl_define_job IMPLEMENTATION.
  METHOD constructor.

    mv_jobname = iv_name.

    CALL FUNCTION 'JOB_OPEN'
      EXPORTING
        jobname          = iv_name
      IMPORTING
        jobcount         = mv_jobcount
      EXCEPTIONS
        cant_create_job  = 1
        invalid_job_data = 2
        jobname_missing  = 3
        OTHERS           = 4.
    IF sy-subrc = 0.
      CALL FUNCTION 'JOB_SUBMIT'
        EXPORTING
          authcknam               = sy-uname
          jobcount                = mv_jobcount
          jobname                 = iv_name
          report                  = iv_report
          variant                 = iv_variant
        EXCEPTIONS
          bad_priparams           = 1
          bad_xpgflags            = 2
          invalid_jobdata         = 3
          jobname_missing         = 4
          job_notex               = 5
          job_submit_failed       = 6
          lock_failed             = 7
          program_missing         = 8
          prog_abap_and_extpg_set = 9
          OTHERS                  = 10.
      IF sy-subrc <> 0.
        RAISE EXCEPTION TYPE lcx_job.
      ENDIF.
    ENDIF.
  ENDMETHOD.
  METHOD start.

    CALL FUNCTION 'JOB_CLOSE'
      EXPORTING
        jobcount             = mv_jobcount
        jobname              = mv_jobname
        pred_jobcount        = iv_pred_jobcount
        pred_jobname         = iv_pred_jobname
        strtimmed            = SWITCH #( iv_pred_jobcount WHEN space THEN 'X' ELSE space )
      IMPORTING
        job_was_released     = mv_released
      EXCEPTIONS
        cant_start_immediate = 1
        invalid_startdate    = 2
        jobname_missing      = 3
        job_close_failed     = 4
        job_nosteps          = 5
        job_notex            = 6
        lock_failed          = 7
        invalid_target       = 8
        OTHERS               = 9.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE lcx_job.
    ENDIF.
  ENDMETHOD.
ENDCLASS.


START-OF-SELECTION.

  TRY.

      DATA(job1) = NEW lcl_define_job(
        iv_name    = nam1
        iv_report  = rep1
        iv_variant = var1 ).
      job1->start( ).

      IF rep2 IS NOT INITIAL.
        DATA(job2) = NEW lcl_define_job(
          iv_name    = nam2
          iv_report  = rep2
          iv_variant = var2 ).
        job2->start(
          iv_pred_jobname  = job1->mv_jobname
          iv_pred_jobcount = job1->mv_jobcount ).
      ENDIF.

      IF rep3 IS NOT INITIAL.
        DATA(job3) = NEW lcl_define_job(
          iv_name    = nam3
          iv_report  = rep3
          iv_variant = var3 ).
        job3->start(
          iv_pred_jobname  = job2->mv_jobname
          iv_pred_jobcount = job2->mv_jobcount ).
      ENDIF.

      IF rep4 IS NOT INITIAL.
        DATA(job4) = NEW lcl_define_job(
          iv_name    = nam4
          iv_report  = rep4
          iv_variant = var4 ).
        job4->start(
          iv_pred_jobname  = job3->mv_jobname
          iv_pred_jobcount = job3->mv_jobcount ).
      ENDIF.

      IF rep5 IS NOT INITIAL.
        DATA(job5) = NEW lcl_define_job(
          iv_name    = nam5
          iv_report  = rep5
          iv_variant = var5 ).
        job5->start(
          iv_pred_jobname  = job4->mv_jobname
          iv_pred_jobcount = job4->mv_jobcount ).
      ENDIF.
    CATCH lcx_job.
      MESSAGE 'Error job creation!' TYPE 'I'.
  ENDTRY.
Enno Wulff