Resume code execution after an exception has been thrown can be very useful in several different scenarios (i.e. processing multiple materials and you want to process all but skip only those where there’s an error) This can be solved quite easily using resumable exceptions
First we define our exception class (can be global – defined in DDIC or locally defined). Nothing special needs to be specified.
CLASS lcx_my_exception DEFINITION
INHERITING FROM cx_static_check.
PUBLIC SECTION.
DATA:
message TYPE char50.
METHODS:
constructor
IMPORTING
message TYPE char50 OPTIONAL.
ENDCLASS. "LCX_MY_EXCEPTION DEFINITION
CLASS lcx_my_exception IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
me->message = message.
ENDMETHOD. "CONSTRUCTOR
ENDCLASS. "LCX_MY_EXCEPTION IMPLEMENTATION
We will demonstrate both the classic exception and resumable exception on a division operation with methods DIVIDE and DIVIDE_RESUMABLE
CLASS lcl_math IMPLEMENTATION.
METHOD divide_resumable.
TRY.
IF i_denominator = 0.
RAISE RESUMABLE EXCEPTION TYPE lcx_my_exception
EXPORTING
message = 'Divide by zero with the following values:'.
ELSE.
result = i_numerator / i_denominator.
ENDIF.
WRITE:/ 'Numerator:', i_numerator,
'Denominator:', i_denominator.
ENDTRY.
ENDMETHOD. "divide_resumable
METHOD divide.
IF i_denominator = 0.
RAISE EXCEPTION TYPE lcx_my_exception
EXPORTING
message = 'Divide by zero with the following values:'.
ELSE.
result = i_numerator / i_denominator.
ENDIF.
* !!! NOT RESUMABLE !!!
* The following line will never be hit when denominator = 0
WRITE:/ 'Numerator:', i_numerator,
'Denominator:', i_denominator.
ENDMETHOD. "divide
ENDCLASS. "lcl_math IMPLEMENTATION
Note the different raising commands in:
Method DIVIDE_RESUMABLE
RAISE RESUMABLE EXCEPTION TYPE lcx_my_exception
Method DIVIDE
RAISE EXCEPTION TYPE lcx_my_exception
Here follows a demo program that calls the division operation in both resumable and non-resumable form:
DATA:
gr_ex TYPE REF TO lcx_my_exception,
g_result TYPE anzh2,
g_err TYPE abap_bool.
START-OF-SELECTION.
TRY.
g_result = lcl_math=>divide(
i_numerator = 10
i_denominator = 0
).
* !!! NOT RESUMABLE !!!
* The following CASE block will never be executed when denominator = 0
CASE g_err.
WHEN space.
WRITE:/ 'Result:', g_result.
WHEN OTHERS.
WRITE:/ 'Result undefined'.
ENDCASE.
CATCH lcx_my_exception INTO gr_ex.
g_err = abap_true.
WRITE:/ gr_ex->message.
ENDTRY.
SKIP 1.
CLEAR:
g_err,
gr_ex.
TRY.
g_result = lcl_math=>divide_resumable(
i_numerator = 10
i_denominator = 0
).
CASE g_err.
WHEN space.
WRITE:/ 'Result:', g_result.
WHEN OTHERS.
WRITE:/ 'Result undefined'.
ENDCASE.
CATCH BEFORE UNWIND lcx_my_exception INTO gr_ex.
WRITE:/ gr_ex->message.
IF gr_ex->is_resumable = abap_true.
g_err = abap_true.
RESUME.
ENDIF.
ENDTRY.
Note the different catch commands for:
Method DIVIDE_RESUMABLE (calls RESUME which returns to previous processing right after command which raised this exception)
CATCH BEFORE UNWIND lcx_my_exception INTO gr_ex.
...
RESUME
Method DIVIDE
CATCH lcx_my_exception INTO gr_ex.
And the output of this simple from is shown on follwing screenshot
This Definition part is missing:
CLASS lcl_math DEFINITION .
PUBLIC SECTION.
METHODS: divide_resumable IMPORTING i_numerator TYPE I
i_denominator TYPE I
RETURNING VALUE(RESULT) TYPE I RAISING RESUMABLE(lcx_my_exception) ,
divide IMPORTING i_numerator TYPE I
i_denominator TYPE I
RETURNING VALUE(RESULT) TYPE I RAISING lcx_my_exception.
endclass.