Clases de Excepcion en ABAP: CX_, TRY-CATCH y Manejo de Errores

Kategorie
ABAP-OO
Veröffentlicht
Autor
Johannes

Las Clases de Excepcion son el mecanismo moderno de manejo de errores en ABAP. Reemplazan a los codigos de retorno clasicos y permiten manejo de errores estructurado con TRY-CATCH.

Jerarquia de Excepciones

CX_ROOT (Abstracta)
├── CX_STATIC_CHECK - Debe declararse o capturarse
├── CX_DYNAMIC_CHECK - Se verifica en tiempo de ejecucion
└── CX_NO_CHECK - No requiere declaracion
TipoCuando usar
CX_STATIC_CHECKErrores esperados que el llamador debe manejar
CX_DYNAMIC_CHECKErrores de programacion (indice fuera de rango)
CX_NO_CHECKErrores del sistema, raramente manejables

Sintaxis basica

TRY-CATCH

TRY.
" Codigo que puede lanzar excepcion
lo_object->do_something( ).
CATCH cx_some_exception INTO DATA(lx_error).
" Manejar el error
DATA(lv_message) = lx_error->get_text( ).
CLEANUP.
" Siempre se ejecuta si hay excepcion no capturada
" Limpiar recursos
ENDTRY.

Ejemplos

1. Crear clase de excepcion simple

CLASS zcx_order_error DEFINITION
PUBLIC
INHERITING FROM cx_static_check
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_t100_message.
CONSTANTS:
BEGIN OF order_not_found,
msgid TYPE symsgid VALUE 'ZORDER',
msgno TYPE symsgno VALUE '001',
attr1 TYPE scx_attrname VALUE 'MV_ORDER_ID',
attr2 TYPE scx_attrname VALUE '',
attr3 TYPE scx_attrname VALUE '',
attr4 TYPE scx_attrname VALUE '',
END OF order_not_found.
DATA: mv_order_id TYPE string READ-ONLY.
METHODS constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
order_id TYPE string OPTIONAL.
ENDCLASS.
CLASS zcx_order_error IMPLEMENTATION.
METHOD constructor.
super->constructor( previous = previous ).
mv_order_id = order_id.
CLEAR me->textid.
IF textid IS INITIAL.
if_t100_message~t100key = if_t100_message=>default_textid.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
ENDCLASS.

2. Lanzar excepcion

METHOD get_order.
SELECT SINGLE * FROM zorders
WHERE order_id = @iv_order_id
INTO @rs_order.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_order_error
EXPORTING
textid = zcx_order_error=>order_not_found
order_id = iv_order_id.
ENDIF.
ENDMETHOD.

3. Capturar excepcion

TRY.
DATA(ls_order) = lo_service->get_order( '12345' ).
WRITE: / 'Pedido encontrado:', ls_order-order_id.
CATCH zcx_order_error INTO DATA(lx_error).
WRITE: / 'Error:', lx_error->get_text( ).
WRITE: / 'ID del pedido:', lx_error->mv_order_id.
ENDTRY.

4. Multiples tipos de excepcion

TRY.
lo_service->process_order( iv_order_id ).
CATCH zcx_order_not_found INTO DATA(lx_not_found).
" Manejar pedido no encontrado
WRITE: / 'Pedido no existe:', lx_not_found->mv_order_id.
CATCH zcx_order_locked INTO DATA(lx_locked).
" Manejar pedido bloqueado
WRITE: / 'Pedido bloqueado por:', lx_locked->mv_locked_by.
CATCH zcx_order_error INTO DATA(lx_general).
" Manejar otros errores de pedido
WRITE: / 'Error general:', lx_general->get_text( ).
CATCH cx_root INTO DATA(lx_any).
" Capturar cualquier otra excepcion
WRITE: / 'Error inesperado:', lx_any->get_text( ).
ENDTRY.

5. CLEANUP para limpieza

DATA: lo_file TYPE REF TO zcl_file_handler.
TRY.
CREATE OBJECT lo_file.
lo_file->open( 'data.txt' ).
lo_file->process( ).
lo_file->close( ).
CATCH zcx_file_error INTO DATA(lx_error).
WRITE: / 'Error de archivo:', lx_error->get_text( ).
CLEANUP.
" Se ejecuta si la excepcion no se captura aqui
" y se propaga hacia arriba
IF lo_file IS BOUND.
lo_file->close( ).
ENDIF.
ENDTRY.

6. Relanzar excepcion

METHOD process_with_logging.
TRY.
lo_service->do_something( ).
CATCH zcx_service_error INTO DATA(lx_error).
" Loguear antes de relanzar
log_error( lx_error ).
" Relanzar la misma excepcion
RAISE EXCEPTION lx_error.
" O envolver en otra excepcion
* RAISE EXCEPTION TYPE zcx_wrapper_error
* EXPORTING previous = lx_error.
ENDTRY.
ENDMETHOD.

7. Excepcion con mensaje T100

CLASS zcx_validation_error DEFINITION
PUBLIC
INHERITING FROM cx_static_check
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_t100_message.
INTERFACES if_t100_dyn_msg.
CONSTANTS:
BEGIN OF field_required,
msgid TYPE symsgid VALUE 'ZVAL',
msgno TYPE symsgno VALUE '001',
attr1 TYPE scx_attrname VALUE 'MV_FIELD_NAME',
attr2 TYPE scx_attrname VALUE '',
attr3 TYPE scx_attrname VALUE '',
attr4 TYPE scx_attrname VALUE '',
END OF field_required,
BEGIN OF value_invalid,
msgid TYPE symsgid VALUE 'ZVAL',
msgno TYPE symsgno VALUE '002',
attr1 TYPE scx_attrname VALUE 'MV_FIELD_NAME',
attr2 TYPE scx_attrname VALUE 'MV_VALUE',
attr3 TYPE scx_attrname VALUE '',
attr4 TYPE scx_attrname VALUE '',
END OF value_invalid.
DATA: mv_field_name TYPE string READ-ONLY,
mv_value TYPE string READ-ONLY.
METHODS constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
field_name TYPE string OPTIONAL
value TYPE string OPTIONAL.
ENDCLASS.

8. Encadenamiento de excepciones (previous)

METHOD outer_method.
TRY.
inner_method( ).
CATCH zcx_inner_error INTO DATA(lx_inner).
" Envolver excepcion interna
RAISE EXCEPTION TYPE zcx_outer_error
EXPORTING
textid = zcx_outer_error=>processing_failed
previous = lx_inner.
ENDTRY.
ENDMETHOD.
" Al capturar, se puede acceder a la cadena
TRY.
outer_method( ).
CATCH zcx_outer_error INTO DATA(lx_error).
WRITE: / 'Error externo:', lx_error->get_text( ).
" Acceder a excepcion original
DATA(lx_previous) = lx_error->previous.
IF lx_previous IS BOUND.
WRITE: / 'Causa:', lx_previous->get_text( ).
ENDIF.
ENDTRY.

9. RAISE EXCEPTION vs. RAISE SHORTDUMP

" Excepcion manejable
RAISE EXCEPTION TYPE zcx_business_error.
" Shortdump inmediato (solo para errores criticos)
RAISE SHORTDUMP TYPE cx_sy_program_error
EXPORTING reason = 'Inconsistencia de datos detectada'.

10. Declarar excepciones en firma de metodo

CLASS zcl_order_service DEFINITION.
PUBLIC SECTION.
METHODS:
" CX_STATIC_CHECK debe declararse
get_order
IMPORTING iv_id TYPE string
RETURNING VALUE(rs_order) TYPE ty_order
RAISING zcx_order_not_found
zcx_order_locked,
" CX_DYNAMIC_CHECK no necesita declararse pero puede
calculate
IMPORTING iv_value TYPE i
RETURNING VALUE(rv_result) TYPE i,
" RAISING zcx_division_by_zero <- opcional
" CX_NO_CHECK nunca se declara
process
IMPORTING iv_data TYPE string.
ENDCLASS.

11. COND/SWITCH con excepciones

" COND con THROW
DATA(lv_status_text) = COND string(
WHEN lv_status = 'A' THEN 'Activo'
WHEN lv_status = 'I' THEN 'Inactivo'
ELSE THROW zcx_invalid_status( status = lv_status )
).
" SWITCH con THROW
DATA(lv_priority) = SWITCH i(
lv_code
WHEN 'H' THEN 1
WHEN 'M' THEN 2
WHEN 'L' THEN 3
ELSE THROW zcx_unknown_code( )
).

12. Excepcion en expresion de tabla

" Lanza CX_SY_ITAB_LINE_NOT_FOUND si no existe
TRY.
DATA(ls_item) = it_items[ id = '123' ].
CATCH cx_sy_itab_line_not_found.
WRITE: / 'Item no encontrado'.
ENDTRY.
" Alternativa: DEFAULT para evitar excepcion
DATA(ls_item2) = VALUE #( it_items[ id = '123' ] DEFAULT ls_empty ).

13. Clase de excepcion con metodos auxiliares

CLASS zcx_validation DEFINITION
PUBLIC
INHERITING FROM cx_static_check
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_t100_message.
DATA: mt_errors TYPE string_table READ-ONLY.
METHODS:
constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
errors TYPE string_table OPTIONAL,
get_error_count
RETURNING VALUE(rv_count) TYPE i,
get_all_messages
RETURNING VALUE(rt_messages) TYPE string_table.
ENDCLASS.
CLASS zcx_validation IMPLEMENTATION.
METHOD constructor.
super->constructor( previous = previous ).
mt_errors = errors.
IF textid IS INITIAL.
if_t100_message~t100key = if_t100_message=>default_textid.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
METHOD get_error_count.
rv_count = lines( mt_errors ).
ENDMETHOD.
METHOD get_all_messages.
rt_messages = mt_errors.
ENDMETHOD.
ENDCLASS.

14. Pattern: Result Type (alternativa a excepciones)

" Clase de resultado
CLASS zcl_result DEFINITION.
PUBLIC SECTION.
DATA: is_success TYPE abap_bool READ-ONLY,
value TYPE REF TO data READ-ONLY,
error TYPE REF TO cx_root READ-ONLY.
CLASS-METHODS:
success IMPORTING iv_value TYPE any
RETURNING VALUE(ro_result) TYPE REF TO zcl_result,
failure IMPORTING ix_error TYPE REF TO cx_root
RETURNING VALUE(ro_result) TYPE REF TO zcl_result.
METHODS get_value_or_throw
RETURNING VALUE(rv_value) TYPE any
RAISING cx_root.
ENDCLASS.
" Uso
DATA(lo_result) = lo_service->find_customer( '123' ).
IF lo_result->is_success.
DATA(ls_customer) = lo_result->value->*.
ELSE.
WRITE: / 'Error:', lo_result->error->get_text( ).
ENDIF.

15. Resumable Exceptions

CLASS zcx_resumable DEFINITION
PUBLIC
INHERITING FROM cx_static_check
CREATE PUBLIC.
PUBLIC SECTION.
" Marcar como resumible
INTERFACES if_t100_message.
ENDCLASS.
" Lanzar como resumible
METHOD process.
TRY.
RAISE RESUMABLE EXCEPTION TYPE zcx_resumable.
CATCH BEFORE UNWIND zcx_resumable INTO DATA(lx_res).
" Decidir si continuar
IF can_continue( ).
RESUME. " Continuar despues del RAISE
ELSE.
RAISE EXCEPTION lx_res. " Propagar
ENDIF.
ENDTRY.
ENDMETHOD.

Resumen

ElementoDescripcion
CX_STATIC_CHECKDebe declararse/capturarse
CX_DYNAMIC_CHECKVerificacion en runtime
CX_NO_CHECKSin verificacion
RAISE EXCEPTION TYPELanzar excepcion
CATCH ... INTOCapturar excepcion
CLEANUPLimpieza antes de propagar
previousEncadenar excepciones

Notas importantes / Mejores practicas

  • Heredar de CX_STATIC_CHECK para errores de negocio esperados.
  • Usar mensajes T100 para textos traducibles.
  • Capturar excepciones lo mas especifico posible.
  • Usar previous para mantener la cadena de errores.
  • CLEANUP solo se ejecuta si la excepcion no se captura localmente.
  • No usar excepciones para control de flujo normal.
  • Preferir nombres descriptivos: ZCX_ORDER_NOT_FOUND vs ZCX_ERROR.
  • En RAP: usar las estructuras FAILED y REPORTED en lugar de excepciones.