[SAP ABAP] Exception handling
Classic exception handling
As was mentioned in the introduction, when working with the SAP system, it is rather inevitable to find some old code, and it was most likely created with classic exceptions. Although it is not recommended, and even discouraged, to use them on a regular basis in a new development, there are still several cases where it is the only way to communicate and handle the unexpected behavior of the program.
Prior to the introduction of class-based, object-oriented programming in SAP systems, one of the methods of code modularization was function modules. The reusable code components with a particular task to do were wrapped into pretty simple function calls, and the modules were bundled into function groups. The function modules—despite the fact that they were mostly superseded by the classes and methods—are still necessary when using Remote Function Call (RFCs).
Even if the code is well thought out and well written, it is still necessary to ensure that the system will not fail if something unexpected happens. What is even more important, it is required to prevent the database from having inconsistent data, even if processing is interrupted abruptly. To solve this problem, developers were given the appropriate mechanism of communicating failures or problems to anyone using their modules—including the 
EXCEPTIONS section in the function module interface shown in the following code snippet:CALL FUNCTION 'SAMPLE_FUNCTION'
    EXPORTING
        …
    IMPORTING
        …
    TABLES
        …
    CHANGING
        …
EXCEPTIONS
        …
The 
EXCEPTIONS section of the call is built along similar lines to the preceding ones—it is a list of possible exceptions returned by the function modules, followed by the assignment of a number in the 0-65535 range. A simple list is presented here:CALL FUNCTION 'SAMPLE_FUNCTION'
    some_defined_exception   = 1
    some_other_exception     = 2
    yet_another_exception    = 3
As it is possible for the function module to have a long list of possible exceptions—and not all of them can be handled in a meaningful way—we can use a keyword, 
OTHERS, to assign a single number to all handleable exceptions not mentioned by name. Refer to the following code snippet:CALL FUNCTION 'SAPMLE_FUNCTION'
    EXCEPTIONS
        some_defined_exception = 1
        OTHERS                 = 2.
Note
It is important to observe that omitting the 
OTHERS keyword while not assigning a number to an exception will lead to a runtime error when this particular exception occurs.
Once the function module is executed and the program flow goes back to the caller, the 
sy-subrcsystem variable is set. By default, if the flow was not interrupted by any exception, the sy-subrc value is set to 0. In any other situation, the value is set according to the number specified in the EXCEPTIONS section thus letting the developer act accordingly.
The classic exceptions are the natural extension of the function modules interface, but they can also be incorporated into the class methods (although it is not recommended). For the local classes, the exceptions are also defined in a dedicated section as shown here:
CLASS lcl_class DEFINITION.
    METHODS
        sample_method
            RETURNING value(arg) TYPE I
            EXCEPTIONS some_exception another_exception yet_another_exception
ENDCLASS.
When defining global classes using the Class Builder, the exceptions are defined on the corresponding screen, accessible via the 
Exceptions button:
On the new subscreen, the classic exceptions are created simply by declaring their name and adding a meaningful description. As the classic exceptions and class-based exceptions (described further in this chapter) are mutually exclusive, the 
Exception Classes checkbox needs to be left unmarked, as follows:
Similar to function modules, when calling the method containing the 
EXCEPTIONS section, the developer is responsible for assigning appropriate codes (numbers) to all the exceptions declared, similar to the following code snippet:DATA lr_ref TYPE REF TO lcl_class.
DATA lv_result TYPE I.
    …
CREATE OBJECT lr_ref.
lr_ref->some_method( 
    RECEIVING 
        arg = lv_result
    EXCEPTIONS 
        some_exception        = 1
        another_exception     = 2
        yet_another_exception = 3 ).
Once again, should the exception happen while not declared in the call statement, the runtime error will occur and the program will be terminated immediately.
After the method call is completed, the 
sy-subrc variable is set to 0 for success or to any number assigned to the exception that was raised during the execution.
Classic exceptions should not be considered the exceptions originating from the system core. They are rather a short explanation, or the reason for the premature termination of the method or a function call. Such termination can be the result of preventive steps to avoid system failure, but also—and probably more likely—it is a simple message stipulating that for some reason, the code logic deviates from the designed flow and the valid results cannot be provided.
Due to their logic-specific nature, classic exceptions need to be raised manually in places where they are actually needed. Regardless of whether you're defining a new function module or new class method, there are two ways of raising a classic exception—the first is with the 
RAISE statement:RAISE some_exception.
The second one is an addition to the 
MESSAGE statement:MESSAGE 'Some error message'  TYPE 'E' RAISING some_exception.
Both statements result in the immediate termination of the current processing block. This termination may have several outcomes, depending on the context. If any of these two statements is executed within the function module or class method, and the caller provided an appropriate 
EXCEPTIONS assignment, the program flow resumes right after the call. If they are executed within a subroutine, the interpreter searches the call stack for the first method or function module that wraps the current context.
If there is neither the former nor the latter, the 
RAISE statement causes a runtime error, whereas MESSAGE-RAISING behaves like the standard MESSAGE statement. Otherwise, if either function module or class method is found, its interface is checked for the definition of the exception. Provided the definition is present, the execution flow is resumed after the call. In other cases, where the definition is not available, RAISE results in a runtime error and MESSAGE-RAISING produces a message.
The aforementioned outcomes lead to a recommendation—when the classic exception is required to be raised, it is preferable to use the 
MESSAGE-RAISING clause as it may carry a bit more information (a message) than the RAISING clause alone.
Classic exceptions cannot be used alongside class-based exceptions within the same processing block.
Class-based exceptions
As the object-oriented programming philosophy was incorporated into ABAP programming, the exceptions concept had to evolve as well. As programs grew and used more classes and objects, the exceptions were eventually migrated to class-based programming. There was also a more practical reason for this move-defining exception, since an object allows for passing more detailed information on what happened to the caller, thereby letting the developer define better-tailored reactions.
The newly created exception objects—either standard or custom ones—are children of the same master abstract class, 
CX_ROOT, and therefore all have common attributes: TEXTID, PREVIOUS, and IS_RESUMABLE, and the common methods, GET_TEXT, GET_LONGTEXT, and GET_SOURCE_POSITION.
The 
CX_ROOT class is the most general exception class and, as such, contains very little information about the specific causes of the exception. Thus, there is a whole hierarchy of subclasses grouped into three elements that make up the first tier —CX_STATIC_CHECK, CX_DYNAMIC_CHECK, and CX_NO_CHECK. This separation of the inheritance tree is done as per the requirements in the declaration and checks are performed.
The requirements mentioned—as well as short recommendations on usage—are as follows:
- Exceptions based on 
CX_STATIC_CHECKand its subclasses need to be declared in the method interface. The syntax checks checks whether the caller wrapped the call with the appropriateTRY-CATCHblock to catch the exception. This type of exception should be used when there is no way to prevent the exception and it needs to be forced to handle the exception by the caller explicitly. - The exceptions originating from 
CX_DYNAMIC_CHECKand its children need to be declared in the interface as well. However, there is no syntax check when defining the method call. These exceptions should be raised if the cause is somehow prevented by other means in the application flow, and therefore there is no need to explicitly tell the caller that the exception needs to be handled. More detailed subclasses of this group are, for example,CX_SY_ARITHMETIC_ERROR,CX_SY_CONVERSION_ERROR, orCX_SY_ASSIGN_ERROR. 
- The usage of 
CX_NO_CHECKand its subclasses—as the name suggests—results in exceptions that are not supposed to be declared in the interface but are always propagated. It represents the exceptions that can happen any time and cannot be prevented in any other way. This group contains, for example,CX_SY_REMOTE_CALL_ERRORorCX_BADIsubgroups, andCX_SY_ILLEGAL_HANDLERorCX_SY_NO_HANDLERclasses. 
Although the introduction of standard exception classes is a natural outcome of migrating the ABAP language to object-oriented programming, it is the possibility of creating custom exception classes that make it a powerful tool. The custom exception classes can be created either locally or by using the Class Builder.
The main difference between the custom exception classes and ordinary classes is that the former must be defined as the subclasses of any of the three tier one exception classes (
CX_STATIC_CHECK, CX_DYNAMIC_CHECK, and CX_NO_CHECK). Apart from this requirement, they can be freely extended with the properties and methods needed.
Unlike classic exceptions, class-based exceptions are not a part of the method call. This is partly due to the separation between 
CX_STATIC_CHECK, CX_DYNAMIC_CHECK, and CX_NO_CHECK—not all of them are required to be handled every time. Similar to other object-oriented languages with the exception mechanism, class-based exceptions are caught with the TRY-CATCH block. In ABAP, however, the aforementioned block is further enhanced with several optional context-control statements, shown in the following code snippet:TRY.
    ...execute standard program flow here...
CATCH [BEFORE UNWIND] some_exception another_exception [INTO oref].
    ...react to the exception here...
[CLEANUP [INTO oref].]
    ...cleanup here before passing the exception...
ENDTRY.
The simplest possible declaration of the 
TRY-CATCH block includes only the following statements with the exception name:TRY.
    ...execute standard program flow here...
CATCH some_exception.
    ...react to the exception here...
ENDTRY.
Although this straightforward example is sufficient to pass the syntax check and avoid runtime errors caused by 
some_exception, it is most likely not enough for proper handling in terms of business context or application logs. In order to get more information from the exception, the INTO orefaddition can be used:TRY.
    ...execute standard program flow here...
CATCH some_exception another_exception INTO oref.
    ...react to the exception here...
CATCH yet_another_exception.
    ...react to the exception here...
ENDTRY.
This addition causes the exception object thrown to be stored in the 
oref variable. Keep in mind the hierarchical structure of the exception classes; the oref variable's type must be a superclass of all the exceptions declared in the CATCH statement. Alternatively, it is possible to use DATA(oref) instead, letting the code interpreter decide the variable's type.Note
The type of the 
oref variable declared with DATA(oref) in the INTO clause will be the lowest common superclass of all the exceptions declared in the CATCH statement.
Once the exception object is stored in the variable, it is possible to access its public properties and methods when reacting to the exception. The exact properties and methods are dependent on the type of the exception and can be freely defined when the custom exception classes are used.
If the standard execution block can raise multiple exceptions, and the reaction for any of them must differ, it is possible to use several 
CATCH statements within one TRY-ENDTRY section, as was also shown in the previous code snippet.
The exceptions mechanism in ABAP is designed this way, so when the exception is raised and the flow control is passed to the 
CATCH statement, the context in which it was raised is lost by default. Although not so common, there are some situations that require the preservation of the raising context when executing the reaction block. One particular example is when the exception is thrown is resumable and the flow is indeed supposed to be resumed. For these purposes, the BEFORE UNWINDaddition was introduced:TRY
    ...execute standard flow here...
CATCH BEFORE UNWIND some_exception INTO oref.
    ...react to the exception here...
    RESUME.
ENDTRY.
One more option for flow control when handling exceptions can be added with the 
CLEANUP statement. This optional keyword can be used when the exception raised is not handled directly by the same TRY-CATCH block, but by some other surrounding block, as in the following code snippet:TRY.
    TRY.
        ...execute standard flow here…
        ...some_other_exception is raised…
    CATCH some_exception.
        ...react to some_exception...
    CLEANUP.
        ...cleanup section…
    ENDTRY.
CATCH some_other_exception.
    …react to some_other_exception…
ENDTRY.
When 
some_other_exception is raised, it is not caught by the innermost TRY-CATCH section, but by the outermost, so the structure is syntactically correct. The CLEANUP section is executed immediately after the raising context is deleted and before the outermost CATCH section.
If the catching statement is supplied with the 
BEFORE UNWIND addition, the CLEANUP section is executed after the CATCH section, or, if there is a RESUME statement in the CATCH clause, it is not executed at all.
Raising class-based exceptions is only a little bit more complicated than classic exceptions. In this case, the 
RAISE EXCEPTION statement should be used, with appropriate additions and options when needed.
The appropriate syntax for raising this kind of exception depends on whether the exception object variable was declared prior to raising. In the first scenario, the following syntax is valid:
CLASS lcx_exception DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
...
DATA lx_exception TYPE REF TO lcx_exception.
...
CREATE OBJECT lx_exception.
RAISE EXCEPTION lx_exception.
If the object was not declared, the 
RAISE statement should have the TYPE keyword and should refer to the type instead of the variable, as in the following code snippet:CLASS lcx_exception DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
...
RAISE EXCEPTION TYPE lcx_exception.
The actual values of the exception parameters need to be passed to the exception object during its creation (either with 
CREATE OBJECT or RAISE EXCEPTION TYPE) through the EXPORTING clause:RAISE EXCEPTION TYPE custom_exception_class
    EXPORTING
        param1 = value 1
        param2 = value 2
        ...
.
The values passed here will be accessible within the 
CATCH statement of the first surrounding TRY-CATCH block, suitable for catching this type of exception.
If the exception thrown is not meant to stop the execution flow—and it should be possible to resume it when the exception is handled—the optional 
RESUMABLEclause can be used:RAISE RESUMABLE EXCEPTION TYPE custom_exception_class.
The appropriate 
CATCH BEFORE UNWIND block may contain the RESUME statement.
Many programs in the ABAP environment utilize messages as a way to communicate their current state and issues to the user, or to store information in logs. Thus, class-based exceptions are also prepared to use this mechanism. As long as the exception class is declared with either the 
IF_T100_DYN_MSG or IF_T100_MESSAGE interface, the MESSAGEaddition can be used.
When constructing custom exception classes, these interfaces are included automatically when the following checkbox is checked:
The resulting interfaces tab is as follows:
Using the 
MESSAGE clause similar to the following code snippet will result in the exception object with the fields populated according to the interface rules, with the whole message accessible through the get_text( ) method:RAISE EXCEPTION TYPE sample_exception_class
    MESSAGE
        ID sy-msgid
        TYPE sy-msgty
        NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
Assertions
ABAP programs, as well as pretty much every other program, are more or less based on certain assumptions. For example, the data provided to a method or a function is consistent, that the program behaves as designed, or that the outcome of data processing follows some kind of scheme.
In most cases, these assumptions are so obvious that the developer is not even aware of them when writing a piece of code. Sometimes, however, it may be more important to check whether a certain condition is fulfilled before proceeding further. Using the standard 
IF-ELSE statement for this purpose is sufficient and will do exactly what is needed. However, there is another shorter and more controlled way of achieving this—with assertions.
The assertion in ABAP code is a simple statement saying assert that something fulfills a condition. There is no need to use any sophisticated syntax or build nested 
IF structures for several conditions. What's more, the assertion mechanism is remotely controlled—particular groups of assertions can be turned on or off when needed (without altering the source code) and several different actions can be performed if the assertion fails.
The best way to start working with assertions is to run the Checkpoints that Can Be Activated (SAAB) transaction and create a new checkpoint group:
Assign the new group to the appropriate package and transport request. This object should advance to the productive system to handle failed assertions there as well.
The resulting checkpoint group is ready to be used and the preview shows the screen as follows:
Once the checkpoint group is created, it is time to set up some assertions in the code. To to this, the following syntax is used:
ASSERT [ [ ID groupID [ SUBKEY key] ] [ FIELDS field 1 … ] CONDITION  ] expression
The 
ID addition is used to assign the assertion to the group defined in the SAAB transaction and control it remotely. Although it is possible to omit this addition (resulting in an assertion that is always active), should the assertion fail, the runtime error will be triggered by the non-handleable ASSERTION_FAILED exception. The ID clause can be further specified with the SUBKEY addition—it can be followed by any character string and is used to easier identify assertions in the program.
The optional 
FIELDS addition is used to provide a list of fields (variables) that should be passed to the assertion log if the assertion fails.
The 
CONDITION clause is required if either ID or FIELDS clauses are used. After the CONDITION, one defines the logical expression to be checked at runtime.
Using the checkpoint group defined in SAAB, a simple assertion example may appear, similar to the following code snippet:
ASSERT ID ZSAMPLE_ASSERT SUBKEY 'date assertion' FIELDS sy-datum sy-uzeit CONDITION sy-datum < '19990101'.
As the year 
1999 is obviously in the past, this assertion is meant to fail.
Provided assertions included in the code are assigned to the checkpoint group using the 
ID addition, they are inactive by default. Using the SAAB transaction again, these assertions can be activated temporarily and the required action can be defined. You can choose from four options for foreground processing:- Inactive: The assertions are not checked at all.
 - Break: Failed assertions open the debugger window.
 - Log: The values of variables specified in the 
FIELDSaddition are logged if the assertion fails. - Abort: The 
ASSERTION_FAILEDexception is thrown when the condition is not met and the program is terminated immediately. 
The available options are also shown in the following screenshot:
Note
ABAP programs can be executed as background processes, for which the debugger cannot be opened. Choosing the 
Break option requires additional decisions as to whether background processing should log the assertion failure or whether to abort processing with ASSERTION_FAILED.
Once the decision is made and the save icon is pressed, the user is prompted to choose an activation period. The assertions from within the checkpoint group will automatically turn inactive after the specified time.
Executing the code with the assertion in 
Log mode results in a new entry on the Log tab:
Drilling down the tree eventually shows the details of this particular assertion failure and the values of variables defined in the 
FIELDS clause:
Well-written assertions allow for quick and easy root cause analysis of errors and failures without the need to use debuggers in a productive environment.
The checkpoint group created with SAAB has the option to activate and deactivate 
BREAK-POINT and LOG-POINT,  both assigned to it. The behavior of the former is similar to the assertion in Break mode, and the latter to the assertion in Log mode, but they don't specify additional conditions.Runtime errors
A runtime error in an SAP system can have many causes. A runtime error is a problem whose effect is interrupting the program. The most common reasons for this are as follows:
- Non-handled exceptions.
 - A handleable exception was not handled.
 - A non-handleable exception was raised.
 - An exit message was sent.
 - An assertion failed (assertion in 
ABORTmode). 
The database table 
SNAPTID lists all existing runtime errors—in total, around 2,000.
ABAP Dump Analysis is used to analyze execution errors in the SAP system. It is a very powerful tool often displayed by developers. The user can run this tool with the 
ST22 transaction.  The tool provides a lot of information that is necessary to repair the existing situation. The selection screen is shown after starting the ST22 transaction. The selection screen is shown as follows:
Searching for a specific error becomes easier when the developer has as much information as possible; this can narrow down the search area.
The following code will result in a short dump since the division by 
0 has not been handled:REPORT ztestdump.
DATA : lv_count TYPE i VALUE 4,
        lv_div   TYPE i,
        lv_denom TYPE i VALUE 0.
 lv_div = lv_count / lv_denom.
The previous code was presented for demonstration purposes. The error simulated in this program will help the reader understand the ABAP Dump Analysis tool in the SAP system.
The resulting error log can be analyzed in the transaction described previously. The error log is divided into a tree structure; its main nodes are composed of the following:
System EnvironmentUser ViewABAP Developer ViewBASIS Developer View
Each of the preceding nodes contains subnodes containing important information related to the problem. 
There are two subnodes in the 
System Environment node: User and Transaction and System Environment. The first contains technical information about the system; there is also information about memory usage. In the second section, the user will find information about the User and Transaction. In this subnode, the user can find out which user caused the error and on which mandate they were logged in.  
In this part, the system informs the user what happened in the system at the time of the error. There is also a suggested way to solve the problem, but this information is not usually sufficient for repair. The sample data provided by the SAP system is shown as follows:
The first set of information contains a brief description of the error. This information (as in this case) is often sufficient to locate the source of the problem. An example message is shown here:
The next set of information is more detailed. It contains detailed information about what has happened and in which program. Often, there is also information about the exception that should be used:
Information on where terminated gives information about where the error occurred and in which line of code the program was aborted. The developer also has access to the data included in the system variables. Sample information about the system variables is shown in the following screenshot:
Some of the most important information provided by this tool is 
How to correct the error. This tells you what can be done to fix the error. This is to avoid repeating problems when using the program. In the following example, the system determines which classes to use to handle the exception. This information is important for both experienced programmers and beginners alike. Often, the information contained in this section allows you to eliminate the error. The use of the exception class is also described in this chapter. An example screen is shown in the following screenshot:
All parts of the tool shown here are valid, but rarely give sufficient information for repair. The next point, however, is the most useful for everyday work. Users have access to information about the code in which the error occurred. After double-clicking, you can go to the place where it will be modified if the user has permission. The following screenshot shows what it looks like:
The preceding screenshot, along with the code, proves that the tool allows you to easily find the problem in the code.
This section describes the problems with BASIS. This is very helpful when the error is related to malfunctions on the kernel side. The user gets information about which program was started during the error. This information is rarely important to the programmer, because in most cases, the problem is programming and the solution consists of modifying the existing code. The sample information is shown as follows:
Comments
Post a Comment