The New Class-Based Exception Handling in ABAP – Part 2
The Standard-Text of an Exception – the Method get_text( )
Now let us start by changing something about the error text in our example to make it a bit more specific. Every exception class has a method get_text( ) which provides information about the particular exception. The texts accessed by this method can be kept in the Online Text Repository (and be maintained in the class builder, transaction SE24) or in Table T100 (and be maintained in message class maintenance, transaction SE91) respectively, to make them locale independent.
Use the error text only for purposes of information in case of a short dump or an error message. Never ever parse the error text in your code in order to obtain information you can use for the error handling. Take this information from attributes of the exception.
When you are the one raising an exception, your exception text should be specific enough to provide a comprehensive description of the error. But when you are the one who handles an exception and decide to output some information always consider who will be reading your text. Texts written to a log file for an administrator or a developer should be different from texts written for an end user. The layman will not at all appreciate information like “division by zero in program ZV_AX100 in Include XRZ”, while a developer will surely be interested in it. Keeping in mind the rule of minimal global assumptions, you should, of course, only output some text in the user interface if you know that the component you write is properly positioned to give out some information.
But let us return to our example. To access the text with get_text(), we must first catch the exception into an appropriately typed variable:
DATA: word TYPE string VALUE 'Hallo', ex TYPE REF TO cx_sy_range_out_of_bounds, mes type string. TRY. PERFORM truncate_at USING length CHANGING word. CATCH cx_sy_range_out_of_bounds INTO ex. mes = ex->get_text( ). WRITE mes. ENDTRY.
You may wonder how the predefined system exception object in our example knew the correct offset, length, and length of the accessed object. When raising an exception you can pass values to attributes of the exception object. These attributes in turn can be parameterized in the information text of the exception. In our example the actual values are passed to the system exception when it is raised.
You will learn more about the details of how this is done a bit later when I show you how to create your own exception classes. But before that, you need to have a better understanding of some other aspects of exceptions.
Why Delegate the Handling of an Exception? – a Short Consideration
To a certain degree exception handling is much like solving real-life problems. Often it is better to delegate the solution of a problem to someone better positioned to solve it or to someone responsible for it.
Regarding our example, it would make sense to dispense with the exception handling in our subroutine. If the caller of the subroutine chose the string and the length, the caller is responsible for the error. So why not leave the exception handling to him? With ABAP class-based exceptions this is possible. You can delegate the handling of an exception to higher levels in the call hierarchy.
Remember I have already told you to avoid, wherever possible, assumptions about the callers of your component. So just decide whether or not you are responsible for an exception and can handle it in a semantic sense (of course writing an empty handler will not do). If you are not responsible, somebody up in the call hierarchy will again have to make the same decision whether to handle or delegate the exception. But if you delegate the exception it is not your job to rack your brain over, who eventually handles the exception. I will show you how to do this in the next weblog of this series.
How to Delegate the Handling of an Exception
At runtime an exception that is not handled locally moves up the call stack. This is what is called propagation. In general, exceptions can only leave an interface at runtime if they are declared in the interface (actually this is not true for all exceptions, as I will show later in another weblog. But for now we won’t worry about the unchecked exceptions). So the first thing we have to do is to declare the exception we want to delegate. We extend the interface of the subroutine by adding “raising cx_sy_range_out_of_bounds”:
FORM truncate_at USING length TYPE i CHANGING word TYPE string RAISING cx_sy_range_out_of_bounds. ***This indicates to the caller of the subroutine that he must be prepared to handle an ***exception of the named type. The calling program looks like this: TRY. PERFORM truncate_at USING length CHANGING word. CATCH cx_sy_range_out_of_bounds. WRITE 'wrong offset access'. ENDTRY.
Note that the subroutine which raises the exception can now do without a TRY-ENDTRY block because the whole subroutine is part of the protected area of the calling program.
Why Declare an Exception in the Interface?
You may wonder what exactly is the point of declaring exceptions? Why make the Raising clause part of the interface? Just the way signs such as: “Beware of the Dog” or “Deer Crossing” informs a passer-by which animals to expect, the raising clause tells the caller of a procedure which exceptions need to be taken into account. As the caller of a routine you therefore know which exception you have to handle or delegate.
This is the semantic point of declaring exceptions. Now let us have a look at how this liability is technically enforced.
- At design time the compiler checks that all exceptions in procedures (methods, subroutines, and function-modules) are handled inside the procedure or declared in the interface and thus delegated to the caller.
- The runtime system assures that only exceptions that are declared in the interface can leave a procedure.