Class based exception are more powerful compared to the “legacy” exceptions. But you don’t want to create wrapper around the Exceptions?
In the blog The Harlem Function Module Shuffle on (old (older SDN)) SCN, Paul Hardy showed a good way to wrap the FM calls to be able leverage them in chained method calls and use the class-based exceptions.
His pattern has a method throw_exception_on_error_from which, in particular, I try to avoid. You would ask why?
Lost Benefit by Wrapper
Almost all of Std SAP FMs, BAPIs etc has the similar kind of structure: Use a wrapper subroutine to raise the exception with (or without) message, call this subroutine to all the different places.
So, whenever there is an short-dump, developer would have a hard time to find from where the exception is raised.
In ABAP debugger, we have a button “Display Trigger Location” whenever an exception is raised. As name suggests the button shows the exact location of the RAISE statement.
If the wrapper is used, the button “Display Trigger Location” shows the location of the RAISE within the wrapper method. But the button doesn’t show the call stack at all hence benefit is lost.
Checkout this program. Very simple demo of raising the exception from the deep withing the functional methods (SELECT_*) and using the wrapper error methods (ERROR_*) to raise the exception.
CLASS lcl_data DEFINITION. PUBLIC SECTION. METHODS: get_data RAISING zcx_gen_exception. PRIVATE SECTION. METHODS: select_1 RAISING zcx_gen_exception, select_2 RAISING zcx_gen_exception, select_3 RAISING zcx_gen_exception, select_4 RAISING zcx_gen_exception, select_5 RAISING zcx_gen_exception, select_6 RAISING zcx_gen_exception. METHODS: error_a RAISING zcx_gen_exception, error_b RAISING zcx_gen_exception, error_c RAISING zcx_gen_exception. ENDCLASS. START-OF-SELECTION. TRY . DATA(lo_data) = NEW lcl_data( ). lo_data->get_data( ). CATCH zcx_gen_exception INTO DATA(lo_exc). "--- use "Display Trigger Location" ENDTRY. CLASS lcl_data IMPLEMENTATION. METHOD get_data. BREAK-POINT. "F7 from here select_1( ). ENDMETHOD. METHOD select_1. select_2( ). ENDMETHOD. METHOD select_2. select_3( ). ENDMETHOD. METHOD select_3. select_4( ). ENDMETHOD. METHOD select_4. select_5( ). ENDMETHOD. METHOD select_5. select_6( ). ENDMETHOD. METHOD select_6. "raise EXCEPTION type ZCX_GEN_EXCEPTION. error_a( ). ENDMETHOD. METHOD error_a. error_b( ). "wrapper ENDMETHOD. METHOD error_b. "do something and pass the Text ID to _c error_c( ). ENDMETHOD. METHOD error_c. RAISE EXCEPTION TYPE zcx_gen_exception. ENDMETHOD. ENDCLASS.
In the debugger,
when check the trigger location
Now, change few things to raise the exception directly from the method SELECT_6( ), use method ERROR_A( ) as prerequisite builder and remove the RAISE statement from the method ERROR_C( ).
METHOD select_6. error_a( ). raise EXCEPTION type ZCX_GEN_EXCEPTION. ENDMETHOD. METHOD error_a. error_b( ). "wrapper ENDMETHOD. METHOD error_b. "do something and pass the Text ID to _c "error_c( ). ENDMETHOD. METHOD error_c. "RAISE EXCEPTION TYPE zcx_gen_exception. ENDMETHOD. ENDCLASS.
Now use the button display trigger location, much better:
Use a wrapper method to determine different prerequisite for raising the exception but keep the RAISE exception. Keep using the benefit of the debugger.