ABAP TRY CATCH ENDTRY : Gestion des exceptions et traitement des erreurs

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

La structure TRY...CATCH...ENDTRY est le concept moderne de gestion des erreurs en ABAP. Elle permet d’intercepter les erreurs d’exécution (exceptions) et d’y réagir de manière contrôlée, au lieu que le programme plante.

Concept de base

  • Exception : Un objet d’erreur qui est déclenché en cas de problème
  • Bloc TRY : Code susceptible de déclencher des exceptions
  • Bloc CATCH : Code qui traite l’exception
  • Bloc CLEANUP : Code toujours exécuté (travaux de nettoyage)

Syntaxe

Structure de base

TRY.
" Code susceptible de déclencher des exceptions
CATCH <classe_exception> [INTO <reference>].
" Traitement de l'erreur
[CATCH <autre_exception> INTO <reference>.]
" Autre traitement d'erreur
[CLEANUP.
" Travaux de nettoyage (optionnel)]
ENDTRY.

Déclencher une exception

RAISE EXCEPTION TYPE <classe_exception>
[EXPORTING <parametre> = <valeur>].

Champs système

Après un CATCH :

  • La référence d’exception contient les détails de l’erreur
  • sy-subrc n’est pas défini (est pour la gestion d’erreur classique)

Exemples

1. TRY-CATCH simple

DATA: lv_result TYPE i.
TRY.
lv_result = 10 / 0. " Division par zéro
CATCH cx_sy_zerodivide.
WRITE: / 'Erreur : Division par zéro !'.
ENDTRY.
WRITE: / 'Le programme continue.'.

2. Lire les détails de l’exception

DATA: lx_error TYPE REF TO cx_sy_zerodivide.
TRY.
DATA(lv_result) = 10 / 0.
CATCH cx_sy_zerodivide INTO lx_error.
" Lire le message d'erreur
DATA(lv_message) = lx_error->get_text( ).
WRITE: / 'Erreur :', lv_message.
" Informations supplémentaires
DATA(lv_longtext) = lx_error->get_longtext( ).
ENDTRY.

3. Traiter plusieurs types d’exceptions

DATA: lv_number TYPE i,
lv_text TYPE string VALUE 'ABC'.
TRY.
lv_number = lv_text. " Erreur de conversion
CATCH cx_sy_conversion_no_number INTO DATA(lx_conv).
WRITE: / 'Erreur de conversion :', lx_conv->get_text( ).
CATCH cx_sy_zerodivide INTO DATA(lx_div).
WRITE: / 'Division par zéro :', lx_div->get_text( ).
CATCH cx_root INTO DATA(lx_any).
" Intercepte toutes les autres exceptions
WRITE: / 'Erreur générale :', lx_any->get_text( ).
ENDTRY.

4. Hiérarchie d’exceptions (cx_root)

Toutes les exceptions ABAP héritent de cx_root. La hiérarchie :

cx_root (classe de base)
├── cx_static_check (doit être déclaré ou intercepté)
├── cx_dynamic_check (devrait être intercepté)
└── cx_no_check (erreurs graves, généralement non interceptables)
TRY.
" Du code quelconque
CATCH cx_static_check INTO DATA(lx_static).
" Intercepte toutes les exceptions vérifiables statiquement
CATCH cx_dynamic_check INTO DATA(lx_dynamic).
" Intercepte toutes les exceptions vérifiables dynamiquement
CATCH cx_root INTO DATA(lx_all).
" Intercepte vraiment toutes les exceptions
ENDTRY.

5. Déclencher et déclarer une exception dans une méthode

CLASS lcl_validator DEFINITION.
PUBLIC SECTION.
METHODS: validate_age IMPORTING iv_age TYPE i
RAISING cx_parameter_invalid.
ENDCLASS.
CLASS lcl_validator IMPLEMENTATION.
METHOD validate_age.
IF iv_age < 0 OR iv_age > 150.
RAISE EXCEPTION TYPE cx_parameter_invalid
EXPORTING
parameter = 'AGE'.
ENDIF.
ENDMETHOD.
ENDCLASS.
" Utilisation
DATA: lo_validator TYPE REF TO lcl_validator.
lo_validator = NEW #( ).
TRY.
lo_validator->validate_age( -5 ).
WRITE: / 'L\'âge est valide.'.
CATCH cx_parameter_invalid INTO DATA(lx_invalid).
WRITE: / 'Paramètre invalide :', lx_invalid->get_text( ).
ENDTRY.

6. Créer sa propre classe d’exception

" Définir une classe d'exception
CLASS lcx_customer_not_found DEFINITION
INHERITING FROM cx_static_check.
PUBLIC SECTION.
INTERFACES: if_t100_message.
METHODS: constructor IMPORTING iv_customer_id TYPE i
!previous TYPE REF TO cx_root OPTIONAL,
get_customer_id RETURNING VALUE(rv_id) TYPE i.
DATA: mv_customer_id TYPE i READ-ONLY.
ENDCLASS.
CLASS lcx_customer_not_found IMPLEMENTATION.
METHOD constructor.
super->constructor( previous = previous ).
mv_customer_id = iv_customer_id.
ENDMETHOD.
METHOD get_customer_id.
rv_id = mv_customer_id.
ENDMETHOD.
ENDCLASS.
" Utilisation
CLASS lcl_customer_service DEFINITION.
PUBLIC SECTION.
METHODS: get_customer IMPORTING iv_id TYPE i
RETURNING VALUE(rs_customer) TYPE zcustomer
RAISING lcx_customer_not_found.
ENDCLASS.
CLASS lcl_customer_service IMPLEMENTATION.
METHOD get_customer.
SELECT SINGLE * FROM zcustomer
WHERE id = @iv_id
INTO @rs_customer.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE lcx_customer_not_found
EXPORTING
iv_customer_id = iv_id.
ENDIF.
ENDMETHOD.
ENDCLASS.
" Appel
DATA: lo_service TYPE REF TO lcl_customer_service.
lo_service = NEW #( ).
TRY.
DATA(ls_customer) = lo_service->get_customer( 12345 ).
WRITE: / 'Client trouvé :', ls_customer-name.
CATCH lcx_customer_not_found INTO DATA(lx_not_found).
WRITE: / 'Client non trouvé. ID :', lx_not_found->get_customer_id( ).
ENDTRY.

7. CLEANUP pour travaux de nettoyage

Le bloc CLEANUP est exécuté lorsqu’une exception quitte le bloc TRY :

DATA: lo_file TYPE REF TO cl_file.
TRY.
" Ouvrir un fichier
lo_file = NEW #( 'data.txt' ).
lo_file->open( ).
" Traitement (peut déclencher une exception)
lo_file->read( ).
CATCH cx_file_error INTO DATA(lx_file).
WRITE: / 'Erreur de fichier :', lx_file->get_text( ).
CLEANUP.
" Est TOUJOURS exécuté, même en cas d'exception
IF lo_file IS BOUND.
lo_file->close( ).
ENDIF.
ENDTRY.

8. Blocs TRY imbriqués

TRY.
TRY.
DATA(lv_result) = 10 / 0.
CATCH cx_sy_zerodivide.
WRITE: / 'Erreur interne : Division par zéro'.
" Propager l'exception
RAISE EXCEPTION TYPE cx_sy_arithmetic_error.
ENDTRY.
CATCH cx_sy_arithmetic_error INTO DATA(lx_math).
WRITE: / 'Erreur externe :', lx_math->get_text( ).
ENDTRY.

9. RETRY - Répétition après erreur

Avec RETRY, le bloc TRY peut être répété :

DATA: lv_attempts TYPE i VALUE 0,
lv_success TYPE abap_bool VALUE abap_false.
TRY.
lv_attempts = lv_attempts + 1.
WRITE: / 'Tentative :', lv_attempts.
" Erreur simulée dans les 2 premières tentatives
IF lv_attempts < 3.
RAISE EXCEPTION TYPE cx_sy_dyn_call_error.
ENDIF.
lv_success = abap_true.
WRITE: / 'Réussi !'.
CATCH cx_sy_dyn_call_error.
IF lv_attempts < 3.
WRITE: / 'Erreur - nouvelle tentative...'.
RETRY. " Retour au TRY
ELSE.
WRITE: / 'Nombre maximum de tentatives atteint.'.
ENDIF.
ENDTRY.

10. Exception avec RESUMABLE (Reprise possible)

CLASS lcx_warning DEFINITION
INHERITING FROM cx_static_check.
PUBLIC SECTION.
METHODS constructor.
ENDCLASS.
CLASS lcx_warning IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
ENDMETHOD.
ENDCLASS.
CLASS lcl_processor DEFINITION.
PUBLIC SECTION.
METHODS: process RAISING RESUMABLE(lcx_warning).
ENDCLASS.
CLASS lcl_processor IMPLEMENTATION.
METHOD process.
WRITE: / 'Le traitement démarre...'.
RAISE RESUMABLE EXCEPTION TYPE lcx_warning.
WRITE: / 'Le traitement a repris !'.
ENDMETHOD.
ENDCLASS.
" Utilisation
DATA: lo_proc TYPE REF TO lcl_processor.
lo_proc = NEW #( ).
TRY.
lo_proc->process( ).
CATCH BEFORE UNWIND lcx_warning.
WRITE: / 'Avertissement reçu - continuer avec RESUME'.
RESUME. " Continue l'exécution après RAISE
ENDTRY.

Classes d’exceptions standard

ClasseDescription
cx_sy_zerodivideDivision par zéro
cx_sy_arithmetic_errorErreurs arithmétiques
cx_sy_conversion_no_numberConversion chaîne vers nombre
cx_sy_range_out_of_boundsIndex de tableau hors limites
cx_sy_itab_line_not_foundLigne de table non trouvée
cx_sy_ref_is_initialDéréférencement de NULL
cx_sy_move_cast_errorDowncast invalide
cx_sy_dyn_call_errorErreur d’appel dynamique

Gestion des exceptions dans les méthodes

CLASS lcl_example DEFINITION.
PUBLIC SECTION.
" L'exception doit être déclarée (cx_static_check)
METHODS: method_with_exception
RAISING cx_parameter_invalid.
" L'exception ne doit PAS être déclarée (cx_dynamic_check)
METHODS: method_with_runtime_error.
" Propager l'exception
METHODS: wrapper_method
RAISING cx_parameter_invalid.
ENDCLASS.
CLASS lcl_example IMPLEMENTATION.
METHOD method_with_exception.
RAISE EXCEPTION TYPE cx_parameter_invalid.
ENDMETHOD.
METHOD method_with_runtime_error.
DATA(lv_x) = 1 / 0. " cx_sy_zerodivide (dynamic_check)
ENDMETHOD.
METHOD wrapper_method.
" L'exception est propagée
method_with_exception( ).
ENDMETHOD.
ENDCLASS.

TRY-CATCH vs. Gestion d’erreur classique

AspectTRY-CATCHsy-subrc
TypeOrienté objetProcédural
InformationException détailléeCode de retour uniquement
PropagationAutomatique vers le hautVérification manuelle
RecommandationModerne, préféréLegacy, pour Open SQL

Remarques importantes / Bonnes pratiques

  • N’interceptez que les exceptions que vous pouvez traiter de manière sensée.
  • Utilisez des classes d’exception spécifiques, pas seulement cx_root.
  • Créez vos propres classes d’exception pour les erreurs spécifiques au domaine.
  • Utilisez INTO pour lire les détails de l’erreur.
  • Le bloc CLEANUP est important pour la libération de ressources.
  • Déclarez les exceptions dans les signatures de méthodes avec RAISING.
  • cx_static_check nécessite un traitement ou une déclaration explicite.
  • Journalisez les exceptions pour le débogage et la surveillance.
  • Combinez avec CLASS et INTERFACE pour une architecture propre.
  • Après les opérations de base de données avec SELECT, vérifiez toujours sy-subrc.