Instruction RAISE en ABAP : Lever des exceptions (basees sur classes et non-basees sur classes)

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

Ces instructions sont utilisees pour signaler activement une situation d’exception (Exception) dans le deroulement du programme. Vous faites cela typiquement quand :

  • Une erreur survient que l’unite de programme actuelle (methode, module fonction, routine Form) ne peut pas resoudre elle-meme.
  • Un etat inattendu ou invalide est atteint.
  • Une regle metier specifique est violee.

Lever une exception interrompt le flux normal du programme a cet endroit et passe le controle a un bloc de gestion des erreurs special (bloc CATCH pour les exceptions basees sur classes) ou a l’appelant (qui verifie alors sy-subrc pour les exceptions non-basees sur classes). Cela permet une gestion structuree des erreurs.

Il existe deux types principaux d’exceptions et donc deux variantes de RAISE :

1. Exceptions basees sur classes (Moderne, Recommande !)

C’est la methode standard dans l’ABAP moderne oriente objet. Les exceptions sont ici des objets de classes d’exception speciales (qui heritent de CX_ROOT).

Syntaxe

" 1. Lever une nouvelle exception d'une classe :
RAISE EXCEPTION TYPE <ClasseException>
[EXPORTING p1 = v1 p2 = v2 ...] " Passer des parametres au constructeur
[MESSAGE { <msg> " Message depuis objet/interface
| ID <id> TYPE <t> NUMBER <n> [WITH v1 v2 v3 v4] " Message depuis T100
| oref->if_t100_message } ] " Message depuis autre objet
[USING MESSAGE]. " Utiliser le message depuis TEXTID
" 2. Relever un objet exception existant (ex. dans CATCH) :
RAISE EXCEPTION <objet_exception>.

Fonctionnement

  1. Un objet de la <ClasseException> specifiee est cree. Des valeurs peuvent etre passees au constructeur de la classe via EXPORTING pour donner des details sur l’erreur (ex. la valeur erronee, un ID).
  2. Optionnellement, un message de la gestion des messages (SE91) ou d’un autre objet peut etre associe a l’exception via l’ajout MESSAGE (de nombreuses classes d’exception implementent l’interface IF_T100_MESSAGE pour cela).
  3. Le flux normal du programme est arrete.
  4. Le systeme recherche vers le haut dans la pile d’appels un bloc TRY...CATCH qui peut gerer cette classe d’exception (ou l’une de ses superclasses).
  5. Si un bloc CATCH est trouve, son code est execute.
  6. Si aucun bloc CATCH correspondant n’est trouve, cela conduit a une erreur d’execution (dump court).

Classes d’exception : Sont creees dans SE24 et heritent typiquement de

  • CX_STATIC_CHECK : Erreurs qui pourraient potentiellement etre detectees des le developpement par la verification de syntaxe (mais pas necessairement). Doivent normalement etre gerees.
  • CX_DYNAMIC_CHECK : Erreurs qui ne surviennent qu’a l’execution, mais que le programme devrait anticiper et gerer.
  • CX_NO_CHECK : Erreurs graves et inattendues pour lesquelles une gestion n’est pas necessairement prevue (conduit souvent au dump si non explicitement interceptee).
  • Gestion des erreurs : L’appelant intercepte ces exceptions avec TRY. ... CATCH cx_... INTO DATA(lo_exc). ... ENDTRY.

2. Exceptions non-basees sur classes (Obsolete)

C’est l’ancienne methode, utilisee principalement dans les modules fonction et les anciennes methodes.

Contexte : N’a de sens que dans les procedures (modules fonction, methodes, FORMs) qui declarent de telles exceptions dans l’ajout EXCEPTIONS de leur interface.

Syntaxe

RAISE <nom_exception>.

Fonctionnement

  1. L’execution de la procedure est immediatement terminee.
  2. Le systeme definit le champ systeme sy-subrc a une valeur associee a ce <nom_exception> dans l’ajout EXCEPTIONS de l’appelant (CALL FUNCTION ... EXCEPTIONS <nom_exception> = valeur ...).
  3. L’appelant doit verifier sy-subrc apres l’appel pour determiner si cette exception s’est produite.
  • Statut : Considere comme obsolete pour les nouveaux developpements, en particulier dans le contexte oriente objet. Ne devrait etre utilise que lorsque d’anciens modules fonction l’exigent.

Exemples

1 : Lever et intercepter une exception basee sur classe

" Classe d'exception personnalisee (simplifiee)
CLASS zcx_division_by_zero DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
CLASS zcx_division_by_zero IMPLEMENTATION.
ENDCLASS.
START-OF-SELECTION.
DATA dividend TYPE i VALUE 10.
DATA divisor TYPE i VALUE 0.
DATA result TYPE f.
DATA lo_error TYPE REF TO zcx_division_by_zero.
TRY.
IF divisor = 0.
" Lever notre propre exception
RAISE EXCEPTION TYPE zcx_division_by_zero.
ELSE.
result = dividend / divisor.
WRITE: / 'Resultat:', result.
ENDIF.
CATCH zcx_division_by_zero INTO lo_error.
MESSAGE 'Erreur : Division par zero !' TYPE 'E'.
" Optionnel : Acceder aux attributs/methodes de lo_error
ENDTRY.
WRITE / 'Le programme continue apres le bloc TRY.'.

2 : Exception basee sur classe avec message

" Hypothese : Message 010 dans classe ZMSG : 'Le materiel &1 n''est pas actif."
" Hypothese : ZCX_MATERIAL_INACTIVE implemente IF_T100_MESSAGE
DATA lv_matnr TYPE matnr VALUE 'M-INACTIVE'.
DATA lo_mat_error TYPE REF TO zcx_material_inactive.
TRY.
" ... La verification montre que le materiel est inactif ...
RAISE EXCEPTION TYPE zcx_material_inactive
MESSAGE ID 'ZMSG' TYPE 'E' NUMBER '010' WITH lv_matnr.
CATCH zcx_material_inactive INTO lo_mat_error.
MESSAGE lo_mat_error->get_text( ) TYPE 'E'. " Recuperer le message de l'exception
ENDTRY.

3 : Exception non-basee sur classe (concept dans module fonction)

FUNCTION Z_DO_SOMETHING.
*"----------------------------------------------------------------------
*"*"Interface locale :
*" IMPORTING
*" VALUE(INPUT) TYPE I
*" EXPORTING
*" VALUE(OUTPUT) TYPE I
*" EXCEPTIONS
*" INPUT_IS_ZERO " Exception declaree
*"----------------------------------------------------------------------
IF input = 0.
RAISE input_is_zero. " leve l'exception -> sy-subrc est defini chez l'appelant
ELSE.
output = 100 / input.
ENDIF.
ENDFUNCTION.
" Programme appelant :
DATA result TYPE i.
CALL FUNCTION 'Z_DO_SOMETHING"
EXPORTING
input = 0
IMPORTING
OUTPUT = result
EXCEPTIONS
input_is_zero = 1 " Mappe l'exception a sy-subrc = 1
OTHERS = 2.
IF sy-subrc = 1.
MESSAGE 'L''entree pour Z_DO_SOMETHING etait zero !' TYPE 'W'.
ELSEIF sy-subrc = 0.
WRITE: / 'Resultat:', result.
ENDIF.

Remarques importantes / Bonnes pratiques

  • Utilisez les exceptions basees sur classes (RAISE EXCEPTION TYPE ...) pour tous les nouveaux developpements.
  • Definissez des classes d’exception personnalisees parlantes pour les cas d’erreur specifiques de votre application. Derivez-les de CX_STATIC_CHECK ou CX_DYNAMIC_CHECK.
  • Implementez l’interface IF_T100_MESSAGE dans vos classes d’exception pour les associer facilement aux classes de messages (SE91).
  • Passez les informations de contexte pertinentes via l’ajout EXPORTING au constructeur de la classe d’exception.
  • Interceptez les exceptions de maniere ciblee avec TRY...CATCH...ENDTRY la ou elles peuvent etre traitees de maniere sensee. Laissez les exceptions non-gerables conduire consciemment a un dump (en particulier celles basees sur CX_NO_CHECK).
  • Evitez RAISE <nom_exception> dans le nouveau code, sauf pour interagir avec de tres anciens modules fonction.