Class based exception are more powerful compared to the “legacy” exceptions. But you don’t want to create wrapper around the Exceptions?
Preface
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.
In Action
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:
Recommendation
Use a wrapper method to determine different prerequisite for raising the exception but keep the RAISE exception. Keep using the benefit of the debugger.
What is your preferred approach to class exceptions?
I prefer 1 generic exc per domain/big development with a return table. My error flow would go via the exception with the error message(s) in the attached return, only catch when needed. Happy flow reports back success/w/i via return table in method params.
Start of next year we are upgrading to 7.4, finally. Then I can do the cool stuff!!!
great article.
Hello Wouter, I use the TEXT IDs with the message and raise the specific message using the TEXTID when exception has to be raised, and without the wrapper.
For More than one message type of scenario, I use the separate table in my class instead of creating that as the attribute of the exception class.
For caller to know where are the messages, we can provide that as part of the exception say refer to xyz attribute for full log – I haven’t done that yet as so far I have been keep on collecting the messages and write them in the App Log before raising the exception where there are more than 1 messages.
Thanks for bringing up the point.
Hello Steve, this can be also applicable to releases prior to 740. I have only used the inline declaration in the example as they are easy to read and less coding 🙂
Thanks.