Class-based exceptions that occur in procedures (methods, function modules, subroutines) do not necessarily have to be handled here. They can be propagated to the caller of the procedure. If the caller is a procedure and the call takes place in a TRY block, the caller can handle the exception or propagate it to its caller.
The highest level to which exceptions can be propagated is processing blocks without a local data area, in other words, event blocks or dialog modules. Both the exceptions forwarded from called procedures and the exceptions that have occurred in the individual coding must be handled here. Otherwise, a runtime error will occur.
A static constructor of a class cannot usually propagate exceptions, since a user of a class does not know whether it is the first user and whether or not it has to handle any exceptions that are propagated by the static constructor.
Likewise, event handler methods cannot propagate exceptions, since this would be against the event concept. The handler of an event is called up indirectly by the runtime environment when an event takes place. It is completely separate from the event trigger and can, therefore, not forward any exceptions to the trigger. Vice versa, the event trigger can never know which handlers react to the event and can, therefore, not handle their exceptions.
Due to the fact that exceptions are represented by transient objects that are only alive in the internal mode of a program, exceptions cannot be propagated to the caller from within programs that were called with SUBMIT ... AND RETURN or CALL TRANSACTION.
To propagate exceptions from procedures, the RAISINGaddition must usually be used for the definition of the interface of a procedure (except for static constructors and event handlers). This ensures that the caller of a procedure knows which exceptions can occur in a procedure and can handle the exceptions or forward them using the RAISINGaddition.
In the RAISING clause, the classes of all exception classes whose exceptions can arise in the procedure must be counted up (strictly speaking, this only applies to exceptions that require declarations). (For more information, see below.) Superclasses can also be specified here to combine groups of exceptions. The most general superclass, CX_ROOT, and its direct subordinate class, CX_NO_CHECK, however, cannot be specified (see below). Since the order of the CATCH blocks in exception handling is determined by the inheritance hierarchy of the exception classes, exception classes in the RAISING clause also have to be specified in this order. This enables the caller of a procedure to list the CATCH blocks in the correct order just based on its knowledge of the procedure interface. It does not need to look at the exception classes in the Class Builder.
This is handled as follows for the possible procedures:
The RAISING addition is specified in the declaration of a method:
METHODS meth ... RAISING cx_... cx_...
The exception classes whose objects are to be propagated are specified after RAISING, in the order described above. Conventional exceptions with the EXCEPTIONS addition cannot be specified simultaneously.
The RAISING addition cannot be specified directly with the methods of global classes. Instead, the exception classes whose objects are to be propagated must be entered in the exception table of the method in the Class Builder. The Class Builder sorts the specified exceptions in the order described above and generates the corresponding RAISING clause.
The Exception Class checkbox has to be selected in the exception table of the Class Builder for this purpose. Otherwise, the Class Builder views the exceptions entered as conventional exceptions and generates a corresponding EXCEPTIONS clause! The tool does not allow exception classes and conventional exceptions to be used simultaneously.
If an exception requiring a declaration has not been defined in the exception table (see below), the syntax check reports an error, since the RAISING clause is incomplete.
In the same way as global methods in the Class Builder, exceptions in function modules are propagated when they are specified in the Function Builder. The Function Builder has a corresponding tab page that works in the same way as the exception table of a method in the Class Builder. To propagate class-based exceptions, the corresponding checkbox must also be selected here. Otherwise, conventional exceptions are declared.
With subroutines, the RAISING addition is specified in the definition of the subroutine.
FORM form ... RAISING cx_... cx_...
The exception classes whose objects are to be propagated are specified after RAISING. Class-based exceptions are the only exceptions that can be listed in the interface of a subroutine.
Exceptions that occur in a procedure will usually either be handled here or forwarded to a higher level with the RAISING addition via the interface of the procedure. This is supported by the syntax check (for methods) or the extended program check (for function modules or subroutines). For example, the syntax of the following subroutine is incorrect:
raise exception type CX_DEMO_CONSTRUCTOR.
The error can either be removed by handling the exception in the subroutine:
raise exception type CX_DEMO_CONSTRUCTOR.
Or by propagating the exception via the RAISING clause:
raise exception type CX_DEMO_CONSTRUCTOR.
This syntax check and the resulting necessity to handle exceptions or forward them using a declaration in the interface is suitable for most application-specific error situations. There may, however, be exception situations to which this procedure is not suited, such as:
· Exceptions that can occur but that do not have to be handled or declared.
It is not worth making it compulsory to handle or explicitly forward this kind of exception, since the user can establish by means of program logic that the exception will not occur.
· Resource bottlenecks
Handling or declaring all exceptions without fail would mean having to specify these exceptions in almost every interface. This would simply make the programs more illegible rather than more "robust".
Three different types of exception class have been introduced to deal with this problem:
1. Exceptions requiring a declaration that either have to be handled or propagated. This is the standard case described previously.
2. Exceptions that can be declared in the interface if desired.
3. Exceptions that must not be declared in the interface.
This is enabled by three direct subordinate classes of the superclass CX_ROOT:
1. CX_STATIC_CHECK for exceptions that have to be declared.
2. CX_DYNAMIC_CHECK for exceptions that do not have to be declared.
3. CX_NO_CHECK for exceptions that must not be declared.
These three classes and their superclass, CX_ROOT, are abstract. All other exception classes are subordinate classes of these three classes. Depending on the superclass, each exception class belongs to one of the three types.
The associated exception classes are subordinate classes of CX_STATIC_CHECK. For these classes, a corresponding exception must either be handled or forwarded explicitly with the RAISING addition. This is checked in the syntax check. Provided that the messages output by the syntax check or extended program check are considered, exceptions requiring declarations can only lead to runtime errors if they are not handled at the top call level.
At present, only self-defined exceptions for error situations in the application coding are subordinate classes of CX_STATIC_CHECK. There are no predefined exceptions CX_SY_... for error situations in the runtime environment that are subordinate classes of CX_STATIC_CHECK.
The associated exception classes are subordinate classes of CX_DYNAMIC_CHECK. If this kind of exception occurs at runtime, it has to either be handled or explicitly forwarded with the RAISING addition, in the same way as the subordinate classes of CX_STATIC_CHECK. This is, however, not checked in the syntax check. If this kind of exception is neither handled nor forwarded at runtime, a runtime error occurs.
This kind of exception class is useful for potential error situations that do not have to be handled or forwarded, since the program logic can more or less exclude them. Most of the predefined exceptions CX_SY_... for error situations in the runtime environment are subordinate classes of CX_DYNAMIC_CHECK. A typical example of this is class CX_SY_ZERODIVIDE. The user of a division can avoid this exception by ensuring that the divisor is not 0. A declaration requirement for predefined exception classes of the runtime environment would actually be impossible, since each potential exception of each ABAP statement of a procedure would have to be handled or propagated.
Since exceptions of the CX_DYNAMIC_CHECK category do not have to be declared in the interface, the called procedure determines whether they are prevented or handled. The caller cannot assume that this kind of exception is forwarded.
The associated exception classes are subordinate classes of CX_NO_CHECK. This kind of exception can be handled. Otherwise, it is forwarded automatically. The RAISING clause contains the addition CX_NO_CHECK implicitly. This class and its subordinate classes do not need to be and must not be specified. The syntax check, therefore, never finds an error here. All the exceptions of the CX_NO_CHECK category that are not handled in the call hierarchy are automatically assigned to the highest call level and result in a runtime error if they are not caught here.
Some of the predefined exceptions CX_SY_... for error situations in the runtime environment are subordinate classes of CX_NO_CHECK. One example of this is the class CX_SY_EXPORT_TO_BUFFER_NO_MEMORY, which is responsible for a particular resource bottleneck but cannot usually be handled there and then. The caller of a procedure must always take into account the fact that the procedure can propagate exceptions of the CX_NO_CHECK category.
In the RAISING clause of the interface of a procedure, only the exceptions that may actually pass the interface have to be declared in terms of syntax. Exceptions that are already handled in the procedure are irrelevant for the interface. The quantity of all exceptions that have to be declared in the interface can be determined as follows:
All the exceptions of the CX_STATIC_CHECK category that may occur outside of a TRY construct in the procedure – whether as a result of an explicit RAISE EXCEPTION or of a procedure call – that can raise these exceptions themselves according to their RAISING clause.
All the exceptions of the CX_STATIC_CHECK category that occur within a TRY construct in the procedure and are not handled within this TRYconstruct. These are all the exceptions that are not caught by CATCH or exceptions that occur within a CATCH block.
These rules are to be used recursively if TRY constructs occur within TRY or CATCH blocks.
A violation of the interface means that an unhandled exception in a procedure is not forwarded using RAISING. According to the explanations above, this can only occur for exceptions of the category CX_DYNAMIC_CHECK or CX_STATIC_CHECK, since exceptions of the CX_NO_CHECK category are always propagated implicitly. For exceptions of the CX_STATIC_CHECK category, violations can only occur if the syntax error message is ignored.
If the interface is violated in this way, the program is not completed with a runtime error. Instead, a new exception of the predefined class CX_SY_NO_HANDLER, which inherits from CX_NO_CHECK, is raised and a reference is defined to the original exception in its attribute PREVIOUS.
If a CX_SY_NO_HANDLER exception occurs, this usually means that the developer of a procedure has forgotten to handle an exception of the CX_DYNAMIC_CHECK category locally or to prevent this exception. It does not mean that the developer has forgotten to extend the RAISING clause accordingly. A handler for this kind of exception cannot catch the original exception. It can only indicate a programming error in the calling procedure.
Since the interfaces of event handlers and static constructors cannot contain any explicit RAISING clauses, unhandled exceptions of the categories CX_DYNAMIC_CHECK or CX_STATIC_CHECK usually lead to the CX_SY_NO_HANDLER exception, which can be handled in the program.
class A_CLASS definition.
methods FOO importing
P type STRING
class B_CLASS definition.
data A type ref to A_CLASS.
methods BAR raising CX_DEMO_CONSTRUCTOR.
data B type ref to B_CLASS.
create object B.
write 'Catching CX_DEMO_CONSTRUCTOR'. "#EC NOTEXT
class A_CLASS implementation.
raise exception type CX_DEMO_CONSTRUCTOR.
class B_CLASS implementation.
create object A.
A->FOO( 'SOMETHING' ).
This example shows which exceptions in the RAISING clause of the BAR method have to be declared.
The method does not contain a RAISE EXCEPTION statement but the FOO method is called up. According to its interface description, the FOO method can supply the exceptions CX_DEMO_CONSTRUCTOR and CX_DEMO_ABS_TOO_LARGE.
CX_DEMO_ABS_TOO_LARGE is caught explicitly with CATCH, which means that only CX_DEMO_CONSTRUCTOR passes the interface. For this reason, this exception must be declared in the RAISING clause of BAR.