In part 3 of this weblog series about the new class-based exceptions in ABAP you learn how to handle several exceptions in one CATCH-clause, how to deal with different exceptions in different CATCH-clauses and what a cleanup-clause is good for. In the more conceptual part of this weblog I treat some more basic semantic topics about exceptions: Which different routes can you take in handling an exception: What should happen inside an exception handler? When to raise an exception at all?
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:
Raising and Catching Different Exceptions – an Example
In a normal program you are usually confronted with exceptions of different types. So let me show you how to handle exceptions in these situations:
The exceptions from the subroutines in line 2 and line 3 are caught by the CATCH clause in line 5. As the example shows, one CATCH clause can cover many exception classes. The exception cx_ex2 is caught by the clause in line 7, because cx_root is the superclass of all exception classes.
All exceptions are derived from this class. And as each catch clause also catches all exceptions of its subclasses, this clause catches all possible exceptions. So what about the exceptions caught in line 5? Why are they not caught by the clause in line 7 instead?
The answer is simple: I have told you that an exception looks for the first handler that catches it. This means: The order of the catch clauses is important. A bit trickier is this case of nested TRY-ENDTRY-constructs within one procedure or on the call stack. Let us compare these constructs to a set of nested brackets. Each pair of brackets has its own set of handlers uniquely related to the pair. If an exception occurs in line n, first the set of handlers of the innermost TRY-ENDTRY-construct around line n is searched through for a suitable handler. If none is found, the next outer construct is looked through, and so on. Once a suitable handler is found, the flow of control flow processes the code in this handler and then continues after the corresponding ENDTRY.
Obviously, if you wrote the more generic catch clause of type cx_root before the catch clauses that catch more specific exceptions, the more specific catch clauses would never be reached. To forestall this, the compiler checks if the catch clauses are sorted in ascending order. If they aren’t you get a syntax error.
Let’s look at the exception cx_outside which may be raised in the handler in line 6. Do you know that cx_outside is not caught by the CATCH cx_root clause in line 7? Does this surprise you? You probably expected that the statement CATCH cx_root which catches all exceptions caught cx_outside in line 7, but remember: an exception is only caught if it occurs within the protected area. If you recall, the protected section goes from the TRY to the first CATCH clause. In this case the handler is not part of the protected section. Of course you can resolve this by enclosing another TRY-ENDTRY construct in a handler.
A Hint of Caution
By the way, caution should be taken when catching cx_root in your program. Since this is the root class of all exceptions classes, you catch all exceptions whatsoever with it.
It is quite obvious that it hardly makes any sense to catch all possible errors in one clause somewhere in your program. What error handling code could you write to guarantee a semanticly meaningful execution of your program for whatever exceptionable situation occurs? Use this catch clause only at a very high level in the call hierarchy to preclude short dumps. Think of a couple of tests in ABAP Unit: The test driver should proceed with the next test, if some test faces an error.
What Do Exception Handlers Do?
Up until now we haven’t worried about what happens inside an exception handler. To more easily understand this, let us use an analogy of a journey to an error in a program. Suppose you are forced to stop your journey because a bridge over a certain river you need to cross is broken. You cannot proceed along the route you have planned. This is our error situation. How can you cope with this?
So before handling an exception, you have to first get a clear idea of how deeply a particular error impacts the component you develop. As I have told you before, you should avoid assumptions about the global layout of the components that call you. If it is your task to provide the address of a customer and there is none for a particular name, delegate the exception to your caller. It is up to the caller to decide how to go on without an address.
Whether or not to Use an Exception
Stepping back a bit from the specific ways in which you deal with an exception once it occurs, you are faced with a basic decision of whether or not to treat a situation as an exception at all. Except for a syntactical error, a situation usually does not come labeled as an exception. But it is up to you to assess it as an exceptional situation or as part of the normal course of events. As a rule of thumb you might say: If some situation is within the realm of expectation, you had better use a return code to indicate it. Reserve exceptions for those situations that exceed the range of the normal.
The Cleanup Section
Once an exception is raised the control flow continues with the next suitable handler which means that the normal flow of control is changed. There may be good reasons to handle some exceptions on a high level in the call hierarchy. This would have the consequence that one or many procedures end prematurely which in turn may leave the application in an inconsistent state or keep resources unreleased. These are the situations the cleanup clause is designed to deal with.
The cleanup section is between the last handler and the ENDTRY. It is processed only if some conditions are fulfilled:
So let us change our example a bit and have a look at how a CLEANUP block works:
What happens to the exceptions in this example? The cx_sy_range_out_of_bounds exception is caught in line 12. If exception cx_ex1 is raised, the CLEANUP block is processed, because it is caught up in the call hierarchy in line 3. Let us suppose that cx_ex2 is caught nowhere in the whole program. This means: If cx_ex2 is thrown, the CLEANUP block is not processed for this reason.
Some Peculiarities of the Cleanup Block
Let me say a few words about why the CLEANUP block is only processed if an exception is thrown and then caught somewhere up in the call hierarchy. If an exception is not caught at all, you get a short dump. In such a situation you are interested in all traces that might lead to the source of the mistake. Processing the CLEANUP block would mean blurring the very traces you are looking for in this situation.
Never ever leave the CLEANUP clause abnormally with the keywords RETURN, EXIT or CONTINUE. The CLEANUP clause is processed with the promise that the respective exception will be caught and handled in the program. Leaving the CLEANUP clause prematurely could have the consequence of breaking this promise.
Take care: During design time you only get a warning, if the CLEANUP clause might be left abnormally. But at runtime there is a short dump, if such a CLEANUP clause is ever tried to be left abnormally.