Class-Based Exceptions in ABAP – a Sketch of the New Concept
In this series of weblogs I will introduce the class-based exception concept available as of SAP Web AS 6.10. The first weblog sketches how the new concept works, tells you how to plan class-based exception handling, and provides you with a simple code example of how to raise and handle a class-based exception.
This weblog is part of a series on the new class-based exception handling concept in ABAP. To navigate to the other parts of the series use one of the following links:
I will use the term class-based exception throughout these weblogs to differentiate between the new exceptions and the old error handling devices in ABAP, which are also called exceptions. While the old error handling worked with return codes, the new class-based exception handling is a completely new concept.
Think of what you would most likely expect from an error-handling concept: When something goes wrong during program execution, at first you would require the control flow to adapt to the new situation. Secondly, you would surly like to have some information on what went wrong and know the origin of the error. Both of these jobs are done by exception objects. When an exception object is generated and an exception is raised or thrown, the exception serves two functions:
- It changes the control flow.
- It carries information about the details of the error.
How Is an Exception Realized?
Class-based exceptions are objects of different specific exception classes which can use attributes to supply additional information about the error type they represent. You can also pass additional specific details about a particular error to the object at runtime, when the object is generated. This is the information part of an exception.
Once an exception of a particular type or class is raised, the runtime looks for the first suitable handler by going first to the procedure where the exception occurred, and then by going up the call stack. When a suitable handler is found, the control flow proceeds with that handler’s code.
The handler should contain some code to resolve the error: to repair it, to abort some procedures or to end processing of the whole program-component, in which the error occurred. The appropriate handling of an exception depends on a thorough semantic analysis of the kind of error that occurred and its impact.
To do this, first figure out what the effect of an error is, then decide what the best reaction to it is, and only as a third step try to find out how you can implement the error handling.
Think as Locally as Possible</p>
Regarding the scope of this consideration let me utter a warning: If you want to write reusable software, plan the exception handling exclusively for the program segment that you have written. This means: Try to avoid any specific assumptions about the layers that you call and those called by you which go beyond the contract specified in the respective interfaces.
The layers calling your component could potentially change. In such cases your exception handling might not work any longer, if it depends on specific assumption about your caller’s program layout. What this means is that you should only handle the exceptions you can meaningfully deal with and delegate those exceptions that you cannot handle to the callers of your program component
Compare your component behaviour to that of a manufacturer: A manufacturer only relies on suppliers to supply what is specified in some contracts. The manufacturer does not care about where and how the suppliers get their products, as long as they meet the conditions specified in the contract. The same applies to the consumer of the goods of our manufacturer.
Let us call it the rule of minimal global assumptions. Only by sticking to this rule, you can write components that support a working modularization of software. But of course, you should take this rule with a grain of salt. It is a general rule, and in some situations there may be good reasons to deviate from it. For example, sometimes you know for certain that you are writing a program component which is only called by the user interface. But generally you are well advised to stick to the rule of minimal global assumptions.
What Does an Exception Handler Do?
Now after digressing a bit to this above mentioned condition for writing modularized software, let me state another simple, yet important point about exception handling: There should be some coding in the handler to cope with the error that has occurred. Of course, an empty handler is a handler in the syntactic sense. But in the full sense of the word, an empty handler just will not do. For example, if a cache is defunct, the values from this cache that you need should be calculated again in the handler. If a particular semantic unit no longer makes sense, because its preconditions are not fulfilled (no delivery possible without an address), it should be canceled and the program should proceed with the next action not affected by the error.
In case there is no handler at all for an exception in the whole call hierarchy, the program ends with a short dump. This is the way exceptions control the program flow.
The Mechanism of Exceptions – a Simple Picture
Perhaps a simple analogy will help to understand the way exceptions work: Think of an exception of a particular class as an object of a particular shape. Once the exception is thrown, the particularly shaped object flies up the call stack until it finds a slot that matches its shape. This is the handler that catches it. When it is caught, the information encapsulated in the object can be accessed. If there is no suitable handler at all, the program ends with a short dump.
System- and User-Defined Exceptions
There are two flavors of exceptions:
- Those raised by the runtime system, if a error is detected.
- Those generated explicitly in the ABAP code.
A typical exception of the first kind is the division by zero, which is arithmetically not defined and so has no result value. If the ABAP runtime environment is confronted with a division by zero, it stops the computation and throws an exception of type cx_sy_zerodivide.
You use exceptions of the second kind to signify a program state which makes the normal execution impossible. There are situations when the exceptional situation is due to a semantic mistake: The program should not schedule the details of a delivery if the customer has no address. In these cases you raise a user-defined exception. All exception classes in ABAP, whether system exceptions or the user defined ones, are indirectly derived from the class cx_root.
A Simple Example
Before going into the details of class-based exception handling and highlighting its advantages and benefits let us start with a simple and straightforward example:
This subroutine is intended to truncate a word by selecting (length) letters from the left and cutting off the rest. Obviously you get a mistake if you try to grab more letters than the length of the word. If this access fails the runtime environment throws an exception of the type cx_sy_range_out_of_bounds. We catch it and output some information text about what has gone wrong.
The Code to Catch an Exception
Now let me give a more detailed explanation of the code you need to catch an exception. Exceptions can only be handled if they are raised in the protected part of a TRY-ENDTRY construct. The protected part comprises the statements between the TRY and the first exception handler.
A handler consists of a CATCH clause and the following statements up to the next CATCH clause or the ENDTRY (if it is the last handler). The CATCH is always followed by the name of one or more exception classes. A CATCH-clause catches only the exceptions of the classes named in the catch-clause and the exceptions that belong to subclasses of those classes. The statements following the CATCH clause in the handler are executed only if a handler catches an exception. All handlers are positioned between the protected section and the ENDTRY. After a handler is processed, the control flow proceeds to the next statement after the ENDTRY.
Leaving details aside, you should keep in mind the following: Write the code which might throw an exception in a protected section shielded by a TRY-ENDTRY construct. Catch the errors in one or more catch clauses after the protected section. A handler can only handle an exception if the name of its class (or superclass) is part of its CATCH clause. The statements of a handler are only processed under the condition that the respective exception is caught. If no error occurs in the protected section, the statements in the catch clauses are not processed.
So the abstract structure is: