Modernes ABAP

Exceptions

(C) Brandeis Consulting.

Exceptions vs. SY-SUBRC

Programmierung des Happy Path

Teilweise sind bei Rückgabewerten (SY-SUBRC) 50% des Codes mit Fehlerhandling beschäftigt. Mit Exceptions liegt der Fokus auf dem Happy Path, die Fehlerverarbeitung kann man das zentral erledigen.

Exceptions können Logik enthalten

Welche Daten für eine Meldung notwendig sind oder ob sich eine Exception selber in einem Log speichern kann, dass kann ich in der Exception (bzw. einer Superklasse) implementieren.

Fehlerverarbeitung kann zum Aufrufer delegiert werden

Oft spielt der konkrete Grund im Code keine Rolle. Ob das Speichern scheitert, weil die Berechtigung nicht ausreicht, eine Sperre sitzt oder eine Validierung scheitert, das ist oft im Code egal. die Reaktion ist immer gleich: Abbrechen und Meldung ausgeben. Das kann oft eine übergeordnete Methode erledigen.

Exceptions können nicht ignoriert werden

Schon mal gesehen?

if sy-subrc <> 0.
  "ToDo - Fehlerverarbeitung mach ich später...
endif. 
(C) Brandeis Consulting.

Exceptions sind Objekte!

  • Es gibt eine Vererbungshierarchie, die wir nutzen sollten:
    • Drei Grundtypen: CX_NO_CHECK, CX_STATIC_CHECK und CX_DYNAMIC_CHECK
    • Unsere eigenen Sub-Kassen davon. Damit können wir
      • Auf unterschiedliche Situationen passend reagieren und
      • Bei Bedarf aber nur die Superklasse kümmern
  • Wir können im Konstruktor einen Break-Point setzen, wenn wir wissen wollen, wo die Exception erzeugt wird...
  • Zur Erzeugung von Nachrichten können wir eigene Methoden anlegen
(C) Brandeis Consulting.

Vererbungshierarchie der Exceptions

(C) Brandeis Consulting.

Namen und Verhalten der unterschiedlichen Exceptions

Die Namen beziehen sich auf die Prüfung (CHECK) an der Methodengrenze, wenn ein Fehler zur Laufzeit auftritt.

CX_NO_CHECK

Es findet keine Prüfung, ob der Fehler deklariert wurde. Der Fehler kann also überall im Call-Stack abgefangen werden. Falls das nirgends passiert, dann Dump.

CX_STATIC_CHECK

Es wird statisch, also zur Designzeit, geprüft, ob der Fehler deklariert wurde.

Eine nicht deklarierte Exception führt zu einer ungeprüften Exception CX_SY_NO_HANDLER, die häufig zu einem Dump führt.

CX_DYNAMIC_CHECK

Es wird zur Laufzeit geprüft, ob der Fehler deklariert wurde.

Eine nicht deklarierte Exception führt zu einer ungeprüften Exception CX_SY_NO_HANDLER, die häufig zu einem Dump führt.

(C) Brandeis Consulting.

CX_STATIC_CHECK

Vorteile

  • Der Aufrufer wird informiert, dass er sich um die Exception kümmern muss. In der IDE eine Warnung, beim ATC-Check oft ein dunkelroter Fehler

Nachteile
Jede Ebene muss die Exception entweder behandeln oder propagieren:

  • Viele Deklarationen
  • Unflexibel bei Änderungen
  • Der Aufrufer muss wissen, wie er angemessen reagiert
  • Falls die Exception nicht deklariert wird, kommt es mit der weniger aussagekräftige Exception CX_SY_NO_HANDLER zum Dump

Wann verwenden

  • Für Schnittstellen, die auch von anderen Entwicklern/Teams verwendet werden
  • Wo der Aufrufer auf die möglichen Fehler hingewiesen werden soll

I think CX_STATIC_CHECK adds too much noise everywhere it's used. CX_STATIC_CHECK is just like using checked exceptions in Java, it forces people to add empty catch blocks, deal with exceptions that they have no idea how to deal with, or to propagate exceptions in their method signatures needlessly. (https://github.com/SAP/styleguides/issues/138)

(C) Brandeis Consulting.

CX_NO_CHECK

Vorteile

  • Propagieren der Exception nicht nötig, aber möglich
  • Jede Ebene kann selber entscheiden, ob sie eine Fehlerverarbeitung machen möchte
  • Nachträgliche Änderungen sind einfach möglich
  • Ab ABAP 7.55 können diese Exceptions in der Methodensignatur mit RAISING angegeben werden. Das ist aber ohne technische Wirkung.
  • Falls sich niemand um die Behandlung kümmert, kommt es zu einem passenden Dump

Nachteil

  • Falls RAISING nicht angegeben wird, ist nicht klar erkennbar, mit welchen Exceptions ein Aufrufer rechnen muss
  • Vor 7.55 musste die Information über die Exceptions in Kommentaren bzw. ABAPDoc platziert werden

Wann verwenden

  • Wenn eine adequate Behandlung nicht zu erwarten ist. Beispiel: Speichern geht nicht, der Grund (Berechtigung, Sperre, Datenbankfehler) ist dem Programmablauf egal. Er spielt nur für die Meldungen für den User eine Rolle.
  • Interne Verwendung, innerhalb eines Moduls/Teams/Klasse.
(C) Brandeis Consulting.

Beispie ZCX_NO_CHECK

(C) Brandeis Consulting.

CX_DYNAMIC_CHECK

Mostly CX_NO_CHECK. Rarely CX_STATIC_CHECK. Never CX_DYNAMIC_CHECK._

(C) Brandeis Consulting.

Erzeugung einer Exception mit Message

Früher war es üblich, eine Konstante vom Typ T100KEY für jede mögliche Exception vorzusehen.

Mit 7.50? ist es möglich, einfach beim RAISE EXCEPTION TYPE die Message mitzugeben. Das ist für alle Exceptions möglich:

      RAISE EXCEPTION TYPE zcx_nc_validation
              MESSAGE e001(zbc_msg)
                 WITH lv_fieldname
                      lv_value.

"Oder in Langform: 
      RAISE EXCEPTION TYPE zcx_nc_validation
             MESSAGE ID 'ZBC_MSG'
                     NUMBER 001
                     WITH 'Name'
                          'Grummelmumpf'.

Vorteile

  • Einfach und flexibel
  • Verwendungsnachweis der Messages funktioniert
  • Die Nachrichten müssen nicht vorher in der Exception-Klasse als Konstante hinterlegt werden
  • Es können beliebige Nachrichten erzeugt werden
(C) Brandeis Consulting.

Umwandlung von SY-SUBRC zu Exception mit Message

Der Zusatz USING MESSAGE ist eine Kurzform der vorherigen Slide. Damit kann man die Nachichtenvariablen aus der SY-Struktur direkt nutzen:

CALL FUNCTION ...
      ...
  EXCEPTIONS
    fehler_bei_datenbeschaffung = 1.

IF sy-subrc NE 0.
  RAISE EXCEPTION TYPE zcx_validation USING MESSAGE.
ENDIF.

Vorteil

  • Wenig Code, um in die Klassenbasierte Ausnahme zu wechseln
  • Immer der gleiche Codeblock
  • Bei No-Check Exceptions ist die Deklaration nicht notwendig und der Fehler kann weit oben abgefangen werden
(C) Brandeis Consulting.

PREVIOUS

Falls eine Exception auf einer anderen Exception basiert, so sollte diese im optionalen Parameter ´PREVIOUS´ mit angegeben werden:

TRY.
    calculate( ).

  CATCH cx_sy_zerodivide INTO DATA(lx_prev).
    RAISE EXCEPTION NEW zcx_validation( previous = lx_prev ).
ENDTRY.

Vorteil

  • Es lässt sich nachvollziehen, was der ursprüngliche Fehler war
  • Die Position im Quellcode lässt sich von ´PREVIOUS´ abfragen, um die passende Codestelle zu finden
(C) Brandeis Consulting.

Wann soll ich bewusst einen Dump auslösen?

Bei Fehlern, die nicht auftreten dürfen! Beispiele sind: Unerwartete Programmfehler, Programmierfehler, Schnittstellen nicht erreichbar, etc.

Vorteil von Dumps

  • Kann der Benutzer nicht weiterarbeiten
  • Dumps sind schwer zu übersehen. Selbst wenn der Benutzer den Dump nicht meldet, dann sollte ein Admin diesen doch in der ST22 bemerken
  • Wir bekommen also in der IT mit Sicherheit mit, wenn etwas schief läuft
  • Es werden alle relenvanten Kontextinformationen gespeichert

Nachteil

  • Sehen nicht schön aus
  • Benutzer halten Entwickler für unfähig

Auslösen von Dumps

RAISE SHORTDUMP TYPE cx_sy_create_object_error.
(C) Brandeis Consulting.

Soll ich CX_ROOT abfangen?

Damit fängt man 90% aller Dumps ab. Aber nein - das ist normalerweise keine gute Idee.

Was mir mal passiert ist: Der Kunde hatte ein großartiges Framework im BW gebaut. Aber eines Tages hatte ein einzelner Prozesse am Ende einfach nichts (In Zahlen 0) mehr gemacht. Keine Änderungen an den Daten, kein Dump. Einfach nichts. Von heute auf morgen hatte der Prozess einfach nicht mehr funktioniert.

Der Grund: Es wurde CX_ROOT abgefangen. Damit ist eine Exception vom Typ CX_NO_CHECK bei einem Datenbankfehler abgefangen worden. Leider hat man versäumt, die Fehlermeldung auch auszugeben.

(C) Brandeis Consulting.