Klassenbasierte Ausnahmen (TRY - CATCH - ENDTRY)

Aus SAP-Wiki
Zur Navigation springenZur Suche springen

Siehe Fehlerbehandlung.

Siehe Kategorie: Fehlerverarbeitung.

Mit dem Befehl "TRY - CATCH - ENDTRY" lassen sich sehr komfortabel klassenbasierte Ausnahmen abfangen, die durch SAP oder kundeneigenen Programmen/Funktionsbausteinen/Methoden während der Laufzeit im gleichen Programm oder aufgerufenen Programmeinheiten ausgelöst wurden. Der Meldungstext wird durch die Ausnahmeklasse geliefert. Man muss nicht mehr wissen von welchem Programm in der Aufrufhierarchie die Ausnahme ausgelöst wurde.

Mit "txterror = oerror->get_text( )" wird der Fehler in die String-Variable TXTERROR geschrieben und kann dann z B in einer Meldung ausgegeben werden oder z. B. in das Application Log. In einer Meldung ist der Text auf 50 Zeichen beschränkt. Längerer Text wird mit "lv_txterror_long = oerror->get_longtext ( )" ausgelesen und in die Variable "LV_TXTERROR" geschrieben. Sowohl "TXTERROR" als auch "LV_TXTERROR" haben den Datentyp String.

Globale Ausnahmeklassen werden in der Transaktion SE24 (Class Builder) definiert und verwaltet. Alle Ausnahmeklassen sind Erben der gemeinsamen Oberklasse CX_ROOT oder einer ihrer Unterklassen. Meist müssen aber keine eigenen Ausnahmeklassen definiert werden, sondern es können die vorhandenen SAP-Ausnahmeklassen verwendet werden.

Codingvorlage

 DATA: oerror TYPE REF TO cx_root,               "Abstrakte Oberklasse aller globalen Exceptions (s. Anzeige Klasse CX_ROOT in SE80)
       lv_txterror TYPE string,
       lv_txterror_long type string.

 TRY.
     data(lv_zahl) = 4 / 0.                      "auslösendes Ereignis

   CATCH cx_sy_zerodivide INTO oerror.
     lv_txterror_long = oerror->get_longtext( ). "Langtext kann über 50 Zeichen sein

     message lv_txterror_long type 'I'.          "Auch Texte über 50 Zeichen werden ausgegeben
                                                 "A", "E", "I", "S" oder "W" in Großbuchstaben
 ENDTRY.

TRY - ENDTRY

  • Mit "TRY. ... ENDTRY." wird der Codingbereich umschlossen, bei dem ein Fehler auftreten kann.
  • Der Catch-Bereich behandelt den Fehler. Er ist im Try-Block eingeschlossen.
try.
 ...     "möglicher Fehlerauslöser, Methode oder Funktionsbaustein
  catch <fehlerklasse>.  "Abfangen des Fehlers
   ...                  "Behandlung des Fehlers
endtry.

Catch

  • Mit "CATCH <ausnahmeklasse> wird eine bestimmte Ausnahme behandelt. Wenn in TRY-ENDTRY-Bereich diese Ausnahme ausgelöst wird, wird sofort der CATCH-Block angesprungen.
  • Wenn eine andere Ausnahme ausgelöst wird, die nicht im CATCH-Block definiert ist, bricht das Programm mit einem Laufzeitfehler ab.
  • Es können in einem CATCH-Befehle auch mehrere mögliche Ausnahmen abgefangen werden, wie hier beim Funktionsbaustein "FP_FUNCTION_MODULE_NAME".


Im Funktionsbaustein "FP_FUNCTION_MODULE_NAME" sieht man, dass hier 3 verschiedene Ausnahmen ausgelöst werden können.

Catch1.JPG


Die 3 Ausnahmen werden nun direkt nach dem CATCH-Befehl aufgeführt.

 TRY.
     CALL FUNCTION 'FP_FUNCTION_MODULE_NAME'
       EXPORTING
         i_name     = p_form
       IMPORTING
         e_funcname = lv_fm_name.

   CATCH cx_fp_api_repository
         cx_fp_api_usage
         cx_fp_api_internal.
   ...

 ENDTRY.

Ausnahmeklasse CX_ROOT

Alle Ausnahmeklassen leiten sich von der abstrakten Ausnahmeklasse CX_ROOT ab.

Methoden CX_ROOT

  • Wichtig sind hierbei besonders die Methoden GET_TEXT und GET_LANGTEXT, mit dem man aus der Ausnahmeklasse den Ursachentext zur aufgetretenen Ausnahme auslesen kann.

Ausnahmeklasse8.jpg

Unterklassen von CX_ROOT

Unter der Rootklasse CX_ROOT gibt es die abgeleiteten abstrakten Klassen

  • CX_DYNAMIC_CHECK
  • CX_NO_CHECK
  • CX_STATIC_CHECK.

Unter diesen Klassen gibt es weitere Klassen. In der Transaktion SE80 lässt sich diese Hierarchie anzeigen.

Ausnahmeklasse3.jpg


Ableitung von Unterklasse zur Oberklasse CX_ROOT

Bei der Ausnahmeklasse CX_DEMO_CONSTRUCTOR sieht man auch den Verberbungsbaum zur abstrakten Oberklasse CX_STATIC_CHECK und diese zur Klasse CX_ROOT.

KlassenbasAusnahme13.jpg


Ein anderes Beispiel von der Klasse CX_SY_ZERODIVIDE zur Superklasse CX_ROOT.

KlassenbasAusnahme15.JPG


Ausnahmeklasse9.jpg

Identifikation Ausnahmeklasse

Über einen Laufzeitfehler / Shortdump

Es ist unschön, wenn man einen Laufzeitfehler bekommt, weil eine klassenbasierte Ausnahme vom Aufrufer einer Methode oder Funktionsbaustein nicht abgefangen wurde. Aber zumindest sieht man im Text des Laufzeitfehlers sofort die fehlende Ausnahmeklasse.

Wenn z. B. eine Division durch Null außerhalb eines Try-Catch-Endtry gemacht wird, wird ein Laufzeitfehler ausgelöst.

Data: lv_zahl type i.

lv_zahl = 4 / 0.

Im Text wird auf die Ausnahmeklasse CX_SY_ZERODIVIDE hingewiesen. Die kann nun verwendet werden.

Ausnahmeklasse6.JPG


bzw. hier bei einem anderen Fall im Langtext

Ausnahmeklasse1.jpg

Über die Ausnahme der Methode/Funktionsbaustein

CatchB2.jpg


  • In der Methode sieht man die beiden klassenbasierten Ausnahmen, die abzufangen sind.

CatchB3.jpg


  • Die klassenbasierten Ausnahmen werden nun abgefangen

CatchB5.jpg

Über die Erweiterte Syntaxprüfung

  • Die Erweiterte Syntaxprüfung weist einen auf klassenbasierte Ausnahmen hin, die nicht abgefunden wurden.

CatchB1.jpg


  • Doppelklick auf die Meldung springt zum Coding

CatchB2.jpg


  • In der Methode sieht man die beiden klassenbasierten Ausnahmen, die abzufangen sind.

CatchB3.jpg

Über die Hierarchie zur obersten abstrakten Ausnahmeklasse CX_ROOT

Ausnahmeklasse4.jpg


Eine Catch-Anweisung auf CX_SY_ZERODIVE behandelt nun die Ausnahme.

DATA: oerror           TYPE REF TO cx_root,
      lv_txterror      TYPE string,
      lv_txterror_long type string.

Data: lv_zahl type i.

TRY.
    lv_zahl = 4 / 0.

* Die Ausnahmeklasse CX_SY_ZERODIVIDE wird nun ausgelöst
* Das Objekt "oerror" beeinhaltet nun die Fehlerinformation
  CATCH CX_SY_ZERODIVIDE INTO oerror.
    lv_txterror      = oerror->get_text( ).      "Zuweisung Fehlertext der Ausnahmeklasse

*   Der Langtext wird in den String geschrieben (oft über 50 Zeichen)
    lv_txterror_long = oerror->get_longtext ( ). 
    MESSAGE i531(0u) WITH txterror.
ENDTRY.


Nun gibt es keinen Shortdump mehr, sondern eine aussagekräftige Meldung.

Ausnahmeklasse2.jpg

Beispiele Klassenbasierte Ausnahmen

Klassenbasierte Ausnahmen müssen nicht in dem Programm behandelt werden, in dem sie ausgelöst werden. Das hat den großen Vorteil, dass die klassenbasierten Ausnahmen durchgereicht werden können entlang der Aufrufhistorie und von einem übergeordneten Programm behandelt werden können.

Ausnahme CX_SALV_EXISTING

  • In der ALV-Klasse CL_SALv_FUNCTIONS sieht man wie eine klassenbasierte Ausnahme ausgelöst wird.

CatchB6.jpg

Ausnahme CX_DEMO_CONSTRUCTOR

HIer wird eine Ausnahme der Ausnahmeklasse CX_DEMO_CONSTRUCTOR ausgelöst mit dem Befehl

RAISE EXCEPTION TYPE cx_demo_constructor ...

KlassenbasAusnahme2.jpg


Die klassenbasierte Ausnahme muss hier in in der Methode CX unter den Ausnahmen eingetragen sein.

KlassenbasAusnahme8.jpg


Wichtig ist die Checkbox KlassenbasAusnahme9.jpg, die der Methode sagt, dass hier eine klassenbasierte Ausnahme vorliegt. Man muss sich entscheiden innerhalb einer Methode zwischen klassenbasierten Ausnahmen und klassischen Ausnahmen.


Der aufrufende Report ZREBCX kann nun die klassenbasierte Ausnahme der Methode CX der Klasse ZREBERROR behandeln.

KlassenbasAusnahme6.jpg


Bei Funktionsbausteinen gibt es im Reiter "Ausnahmen" die gleiche Checkbox "Ausnameklassen", die für die Erzeugung einer klassenbasierten Ausnahme benötigt wird. Hier beim Funktionsbausetin FP_JOB_OPEN gibt es. nur klassischen Ausnahmen.

KlassenbasAusnahme10.jpg

Beispielcoding

Beispielcoding CX_ROOT

  • Da jede klassenbasierte Ausnahme von der obersten Fehlerklasse CX_ROOT vererbt ist, wird auch immer bei einer klassenbasierten Ausnahme die Fehlerklasse CX_ROOT ausgelöst.
  • Hier hängt die Fehlerklasse CX_SY_ZERODIVIDE in der Hierarchie unter der Fehlerklasse CX_ROOT und bei einer Division durch 0 könnten daher beide Fehlerklassen ausgewertet werden.
TRY.
  data(lv_zahl) = 4 / 0.

  data: lv_txterror_long type string.

  CATCH cx_root INTO data(lo_error).     "cx_sy_zerodivide
    lv_txterror_long = lo_error->get_longtext( ).

    message lv_txterror_long type 'I'.    "Auch Texte über 50 Zeichen
ENDTRY.

Beispielcoding CX_SY_ZERODIVIDE

Hier beim nachfolgenden Beispiel bei einer Division durch Null kann es passieren, dass ein Laufzeitfehler entsteht. Damit das Programm nicht mit einem Laufzeitfehler abbricht, wird eine Try-Endtry-Routine eingesetzt.

Das meiste Coding kann im Try-Catch-Endtry bei jedem Fehler gleich verwendet werden und insofern kann man das Coding zunächst gut mit einer Vorlage wie dieser einfügen und dann nur das Coding mit dem fehlerauslösenden Ereignis austauschen.

Die Fehlerklasse, in diesem Fall "CX_SY_ZERODIVIDE" sieht man im Text des Laufzeitfehlrs, wenn einmal der Shortdump nicht durch den Try-Catch-Endtry abgefangen wurde.

  DATA: oerror TYPE REF TO cx_root,  "Abstrakte Oberklasse aller globalen Exceptions (s. Anzeige Klasse CX_ROOT in SE80)
        lv_txterror TYPE string,
        lv_txterror_long type string.

  DATA: lv_zahl TYPE i.

  TRY.
      lv_zahl = 4 / 0.

    CATCH cx_sy_zerodivide INTO oerror.
      lv_txterror = oerror->get_text( ).
      lv_txterror_long = oerror->get_longtext( ). "Langtext ist oft über 50 Zeichen

     "MESSAGE i531(0u) WITH lv_txterror.

      message lv_txterror_long type 'I'.          "Auch Texte über 50 Zeichen werden ausgegeben

  ENDTRY.

Beispielcoding Dynamische Programmierung

Beim nachfolgenden Beispiel mit einer dynamischen Programmierung kann es passieren, dass Selektionsfelder übergeben oder Where-Felder abgefragt werden, die nicht existieren. Da diese Felder erst zur Laufzeit bekannt sind, kann die Syntaxprüfung diese Fehler nicht vorher abfangen.

Die Ausnahmeklasse CX_SY_DYNAMIC_OSQL_SEMANTICS löst im Fehlerfall eine Ausnahme aus, die dann behandelt werden kann.

DATA: oerror TYPE REF TO cx_root,
      txterror TYPE string.

 TRY.
     SELECT (gt_adressenfelder)
       INTO TABLE p_ct_itab
       FROM ztest
       WHERE (gt_where).

   CATCH cx_sy_dynamic_osql_semantics INTO oerror.
     txterror = oerror->get_text( ).
     MESSAGE i531(0u) WITH txterror.
 ENDTRY.

Constructor Ausnahmeklasse

Jede SAP-Ausnahmeklasse hat eine Constructor-Methode.

KlassenbasAusnahme11.jpg


KlassenbasAusnahme12.jpg


Hier wird der Constructor der Ausnahmeklasse CX_DEMO_CONSTRUCTOR mit einem Parameter MY_TEXT aufgerufen und SY-REPID übergeben.

KlassenbasAusnahme2.jpg


Das aktuelle Programm ist letztlich ein Bestandteil der Fehlerbeschreibung.

KlassenbasAusnahme7.jpg

Try- und Catch-Behandlung in einem Programm

  • Die Ausnahme wird in der Laufzeitumgebung von SAP ausgelöst oder über den Befehl "Raise Exception" im SAP-Programm.
REPORT demo_raise_exceptions.
DATA oref TYPE REF TO cx_root.
DATA text TYPE string.

TRY.
    RAISE EXCEPTION TYPE cx_demo_constructor
      EXPORTING
        my_text = sy-repid.
    CATCH cx_demo_constructor INTO oref.
      text = oref->get_text( ).
      WRITE / text.
ENDTRY.

Laufzeitfehler

  • Wirft ein Programm eine klassenbasierte Ausnahme mit "raise exception", dann muss dieses Programm oder ein anderes Programm in der Aufrufhierarchie diese Ausnahme behandeln. Erfolgt dies nicht, kommt es zu einem Laufzeitfehler
  • Hier ruft ein Report eine Methode CX der Klasse ZREBERROR auf, in der die klassenbasierte Ausnahme ausgelöst wird. Zur Provokation des Laufzeitfehlers wurde hier innerhalb des Try-Blocks der Catch auskommentiert.

KlassenbasAusnahme1.jpg


KlassenbasAusnahme2.jpg


KlassenbasAusnahme3.jpg


Mit dem Button KlassenbasAusnahme5.jpg kommt man zu einer ausführlicheren Beschreibung der Ausnahme.

KlassenbasAusnahme4.jpg


Sobald die Catch-Behandlung wieder aktiv ist, wird auch kein Laufzeitfehler mehr ausgelöst.

KlassenbasAusnahme6.jpg


KlassenbasAusnahme7.jpg

Web-Links

Literatur