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.

marp: true
theme: bcg_slide_theme
paginate: true
footer: “(C) Brandeis Consulting”
headingDivider: 1

Modern ABAP

Exceptions

(C) Brandeis Consulting

Exceptions vs. SY-SUBRC

Programming the happy path

In some cases, 50% of the code is occupied with error handling for return values (SY-SUBRC). With exceptions, the focus is on the happy path, and error processing can be handled centrally.

Exceptions can contain logic

I can implement in the exception (or a superclass) which data is necessary for a message or whether an exception can save itself in a log.

Error processing can be delegated to the caller

Often, the specific reason in the code is irrelevant. Whether the save fails because the authorization is insufficient, a lock is in place, or a validation fails, it often doesn't matter in the code. The response is always the same: abort and issue a message. This can often be handled by a higher-level method.

Exceptions cannot be ignored

Ever seen this?

if sy-subrc <> 0.
"ToDo - I'll do error handling later...
endif.
(C) Brandeis Consulting

Exceptions are objects!

  • There is an inheritance hierarchy that we should use:
  • Three basic types: CX_NO_CHECK, CX_STATIC_CHECK, and CX_DYNAMIC_CHECK
  • Our own subclasses of these. This allows us to
  • Respond appropriately to different situations and
  • Only use the superclass if necessary
  • We can set a breakpoint in the constructor if we want to know where the exception is generated...
  • We can create our own methods for generating messages.
(C) Brandeis Consulting

Inheritance hierarchy of exceptions.

(C) Brandeis Consulting

Names and behavior of the different exceptions

The names refer to the check (CHECK) at the method boundary when an error occurs at runtime.

CX_NO_CHECK

There is no check to see if the error has been declared. The error can therefore be caught anywhere in the call stack. If this does not happen anywhere, then dump.

CX_STATIC_CHECK

A static check, i.e., at design time, is performed to determine whether the error has been declared.

An undeclared exception leads to an unchecked exception CX_SY_NO_HANDLER, which often results in a dump.

CX_DYNAMIC_CHECK

A check is performed at runtime to determine whether the error has been declared.

An undeclared exception leads to an unchecked exception CX_SY_NO_HANDLER, which often results in a dump.

(C) Brandeis Consulting

CX_STATIC_CHECK

Advantages

  • The caller is informed that they must handle the exception. In the IDE, a warning is displayed; in the ATC check, often a dark red error.

Disadvantages
Each level must either handle or propagate the exception:

  • Many declarations
  • Inflexible when changes are made
  • The caller must know how to respond appropriately
  • If the exception is not declared, the less meaningful exception CX_SY_NO_HANDLER causes a dump

When to use

  • For interfaces that are also used by other developers/teams
  • Where the caller should be made aware of possible errors

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

Advantages

  • Propagating the exception is not necessary, but possible
  • Each level can decide for itself whether it wants to handle errors
  • Subsequent changes are easy to make
  • As of ABAP 7.55, these exceptions can be specified in the method signature with RAISING. However, this has no technical effect.
  • If no one handles the exception, a suitable dump is generated.

Disadvantage

  • If RAISING is not specified, it is not clear which exceptions a caller must expect.
  • Prior to 7.55, information about exceptions had to be placed in comments or ABAPDoc.

When to use

  • When adequate handling is not to be expected. Example: Saving is not possible, but the reason (authorization, lock, database error) is irrelevant to the program flow. It only plays a role in the messages for the user.
  • Internal use, within a module/team/class.
(C) Brandeis Consulting

Example ZCX_NO_CHECK

(C) Brandeis Consulting

CX_DYNAMIC_CHECK

Mostly CX_NO_CHECK. Rarely CX_STATIC_CHECK. Never CX_DYNAMIC_CHECK._

(C) Brandeis Consulting

Generating an exception with a message

Previously, it was common practice to provide a constant of type T100KEY for every possible exception.

With 7.50?, it is possible to simply specify the message with RAISE EXCEPTION TYPE. This is possible for all exceptions:

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

"Or in long form:
RAISE EXCEPTION TYPE zcx_nc_validation
MESSAGE ID 'ZBC_MSG'
NUMBER 001
WITH 'Name'
'Grummelmumpf'.

Advantages

  • Simple and flexible
  • Message usage verification works
  • Messages do not have to be stored in advance in the exception class as constants
  • Any messages can be generated
(C) Brandeis Consulting

Conversion from SY-SUBRC to exception with message

The addition USING MESSAGE is a short form of the previous slide. This allows you to use the message variables from the SY structure directly:

CALL FUNCTION ...
...
EXCEPTIONS
error_during_data_retrieval = 1.

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

Advantage

  • Less code to switch to the class-based exception
  • Always the same code block
  • For no-check exceptions, the declaration is not necessary and the error can be caught high up in the code.
(C) Brandeis Consulting

PREVIOUS

If an exception is based on another exception, this should be specified in the optional parameter ´PREVIOUS´:

TRY.
calculate( ).

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

Advantage

  • It is possible to trace what the original error was
  • The position in the source code can be queried from 'PREVIOUS' to find the appropriate code location
(C) Brandeis Consulting

When should I deliberately trigger a dump?

For errors that should not occur! Examples include: unexpected program errors, programming errors, interfaces not accessible, etc.

Advantages of dumps

  • The user cannot continue working
  • Dumps are difficult to overlook. Even if the user does not report the dump, an administrator should notice it in ST22
  • So we in IT are sure to notice when something goes wrong
  • All relevant context information is stored

Disadvantages

  • They don't look nice
  • Users think developers are incompetent

Triggering dumps

RAISE SHORTDUMP TYPE cx_sy_create_object_error.
(C) Brandeis Consulting

Should I catch CX_ROOT?

This intercepts 90% of all dumps. But no—that's not usually a good idea.

What happened to me once: The customer had built a great framework in BW. But one day, a single process simply did nothing (in numbers 0) at the end. No changes to the data, no dump. Just nothing. From one day to the next, the process simply stopped working.

The reason: CX_ROOT was caught. This meant that an exception of type CX_NO_CHECK was caught in the event of a database error. Unfortunately, the error message was not displayed.

(C) Brandeis Consulting