Working with Exception Classes

Objective

After completing this lesson, you will be able to Work with exception classes.

Exception Handling

Earlier in this learning journey, we looked at error handling using the TRY, CATCH, and ENDTRY statements. After the TRY statement comes the statement that might cause an error. If the error does occur, the system jumps to the CATCH statement and processes its contents. Whatever happens, the method carries on processing after the ENDTRY statement.

In a TRY…ENDTRY block, you can include more than one CATCH statement. This allows you to handle different exceptions differently.

You can list multiple exceptions in a single CATCH statement. In this example, the same catch block will be processed if the system triggers either cx_sy_arithmetic_error or cx_sy_zerodivide.

When you list a class in a CATCH statement, the system will process the corresponding block if an exception with that class or one of its subclasses is triggered. In this example, cx_sy_arithmetic_error is a superclass of both cx_sy_arithmetic_overflow and cx_sy_zerodivide. Conseqeuently, the block will be processed if an exception with the type of either of the subclasses is raised.

All exception classes are part of an inheritance hierarchy whose topmost superclass is the class CX_ROOT. As we have already seen, if you use a superclass in a CATCH statement, the system will jump to it if an exception with the type of any of its subclasses is triggered. Since all exception classes are subclasses of CX_ROOT, the statement CATCH CX_ROOT is sufficient to catch any exception.

Sequence of CATCH Statements

When you write CATCH statements, you must place the most specific class (or classes) first and the most generic ones last. If you place a superclass above one of its subclasses, you will cause a syntax error. This is because the system could not process the specific CATCH block because it is overshadowed by the more generic one.

The INTO Addition

Each exception is represented by an object. You can place this object in a reference variable and work with it - for example, to retrieve the error text from the exception class using the get_text( ) method as shown in the example here.

You can use an inline declaration in the CATCH statement to ensure that the reference variable is declared with the correct type.

The most common thing to do with the exception object is to call its get_text( ) method. However, exception objects can possess other attributes and methods to help you handle the error.

Exception classes can have attributes that describe more precisely what went wrong. These attributes are often used to fill the placeholders of messages. You define the attribute in the public section of the exception class with its appropriate name and type.

In order to fill the attribute when the exception is raised, you need a corresponding import parameter of the constructor of the exception class. You can generate the attribute using a quick fix. Place the cursor on the method name constructor and press Ctrl + 1. Choose the quick fix Re-generate constructor.

The Attribute "Previous"

Sometimes within a call chain a method will catch an exception. In response, it will raise a different exception for its own caller to catch. This might happen, for example, if a framework raises an exception that is too technically-oriented for the application to process by itself. However, in certain circumstances the method that does this (method 2 in the example) may want to trigger its own exception, but also pass on the first exception. It can do this using the previous attribute of the exception class.

When you raise an exception, you can pass an instance of an exception class to the importing parameter previous. This attaches the exception object to the new exception. A method that catches the second exception can access the first exception object using the public read-only attributes previous.

Previous is implemented in the superclass CX_ROOT, and is therefore available in all exception classes.

A method does not always have to handle the exceptions of another method that it has called. Instead, it can propagate the method along the call chain. Instead of using the CATCH statement to handle the exception, the method includes the RAISING clause in its definition. This indicates to the callers of the method that the exception can be passed to them.

If the exception is not caught anywhere along the call chain, a runtime error will occur.

Try It Out: Exception Classes

  1. Create a new class that implements the interface IF_OO_ADT_CLASSRUN.
  2. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ).
    Code Snippet
    Copy code
    Switch to dark mode
    12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
    DATA number1 TYPE i VALUE 2000000000. DATA number2 TYPE p LENGTH 2 DECIMALS 1 VALUE '0.5'. DATA result TYPE i. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_overflow. out->write( 'Arithmetic Overflow' ). CATCH cx_sy_zerodivide. out->write( 'Division by zero' ). ENDTRY. number2 = 0. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_overflow. out->write( 'Arithmetic Overflow' ). CATCH cx_sy_zerodivide. out->write( 'Division by zero' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_overflow cx_sy_zerodivide. out->write( 'Arithmetic overflow or division by zero' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_error. out->write( 'Caught both exceptions using their common superclass' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_root. out->write( 'Caught any exception using CX_ROOT' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_root into data(Exception). out->write( 'Used INTO to intercept the exception object' ). out->write( 'The get_Text( ) method returns the following error text: ' ). out->write( Exception->get_text( ) ). ENDTRY.
  3. Activate the class with Ctrl + F3.
  4. Play around with the coding to familiarize yourself with it.

Work with Exception Objects

You improve your code by handling exception objects, that is, instances of exception classes. You use constructor parameters to set attribute values of exception objects.

Template:

  • /LRN/CL_S4D401_OOS_FACTORY (Global Class)

Solution:

  • /LRN/CL_S4D401_EXS_HANDLING (Global Class)

Task 1: Copy Template (Optional)

Copy the template class. If you finished the previous exercise, you can skip this task and continue editing your class ZCL_##_SOLUTION.

Steps

  1. Copy class /LRN/CL_S4D401_OOS_FACTORY to a class in your own package (suggested name: ZCL_##_SOLUTION, where ## stands for your group number).

    1. In the Project Explorer view, right-click class /LRN/CL_S4D401_OOS_FACTORY to open the context menu.

    2. From the context menu, choose Duplicate ....

    3. Enter the name of your package in the Package field. In the Name field, enter the name ZCL_##_SOLUTION, where ## stands for your group number.

    4. Adjust the description and choose Next.

    5. Confirm the transport request and choose Finish.

  2. Activate the copy.

    1. Press Ctrl + F3 to activate the class.

Task 2: Access Exception Objects

In your global class, adjust the implementation of method IF_OO_ADT_CLASSRUN~MAIN. Extend the exception handling when calling the factory method GET_INSTANCE. In both CATCH blocks, receive a reference to the exception object and extract the exception text. Replace the literal text for the console output with the exception texts.

Steps

  1. Navigate to the implementation of method IF_OO_ADT_CLASSRUN~MAIN of your global class.

    1. Open the global class and switch to the Global Class tab.

  2. In the first CATCH statement, let a reference variable point at the exception object, that is the instance of exception class CX_ABAP_INVALID_VALUE (suggested name for the variable: exc_inv .

    Hint

    You can use an inline declaration to declare the reference variable with the correct type.
    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CATCH cx_abap_invalid_value .
    2. To display the type of variable exc_inv place the cursor on exc_inv and press F2.

  3. In this catch block, comment out the call of method out->write. Retrieve the exception text from the exception object and use it in a new call of method out_>write.

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CATCH cx_abap_invalid_value INTO DATA(exc_inv). out->write( | Carrier { c_carrier_id } does not exist | ).
  4. Do the same with the second CATCH statement and CATCH block (suggested name for the reference variable: exc_auth).

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CATCH cx_abap_auth_check_exception . out->write( | No authorization to display carrier { c_carrier_id } | ).
  5. Activate and test your code. Adjust the value of constant c_carrier_id to provoke the different exceptions.

    1. Press Ctrl + F3 to activate the code.

    2. Press F9 to execute the code as console app.

    3. Set the value of constant c_carrier_id to XX to provoke the exception for invalid value.

    4. Set the value of constant c_carrier_id to UA to provoke the exception for missing authorization.

Task 3: Handle Groups of Exceptions

In local test class LTCL_FIND_FLIGHTS, adjust the implementation of method CLASS_SETUP. When calling factory method GET_INSTANCE, handle all possible exceptions in just one CATCH block. In case of an exception, supply the ASSERT method with the exception text from the exception object, and not with the generic error message "Unable to instantiate lcl_carrier") .

Steps

  1. Navigate to the implementation of method CLASS_SETUP of local test class LTCL_FIND_FLIGHTS.

    1. Display the Test Classes tab.

    2. Scroll down to statement METHOD class_setup.

  2. After the call of method GET_INSTANCE, adjust the exception handling. Comment the catch block for exception class CX_ABAP_INVALID_VALUE and replace exception class CX_ABAP_AUTH_CHECK_EXCEPTION with CX_ROOT.

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CATCH cx_abap_invalid_value. cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ). CATCH cx_abap_auth_check_exception.
  3. Add an INTO clause to the CATCH statement with an inline declared reference variable (suggested name for the variable: exc_root .

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CATCH cx_root .
    2. To display the type of variable exc_root place the cursor on exc_root and press F2.

  4. In this catch block, use the exception text as input for method CL_ABAP_UNIT_ASSERT=>fail.

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123456
      CATCH cx_root into DATA(exc_root). cl_abap_unit_assert=>fail( ).
  5. Activate your code.

    1. Press Ctrl + F3.

Task 4: Set Attributes of Exception Objects

Adjust the raising of the exceptions in factory method GET_INSTANCE. Where you raise exception CX_ABAP_INVALID_VALUE, use an appropriate constructor parameter to include the invalid carrier ID in the exception text. Where you raise exception CX_ABAP_AUTH_CEHCK_EXCEPTION, use an appropriate constructor parameter to choose a more appropriate exception text.

Steps

  1. Navigate to the implementation of method GET_INSTANCE of local class LCL_CARRIER.

    1. For example, place the cursor on get_instance and press F3.

  2. Analyze the signature of the CONSTRUCTOR method of exception class CX_ABAP_INVALID_VALUE.

    1. In the RAISE EXCEPTION statement, place the cursor on CX_ABAP_INVALID_VALUE and press F2 to display the code element information for the exception class.

    2. On the dialog window, choose constructor to display the code element information for the CONSTRUCTOR method.

  3. Adjust the RAISE EXCEPTION statement for exception class CX_ABAP_INVALID_VALUE. Supply the constructor parameter value with the invalid carrier ID.

    Note

    You have to perform a type conversion because value and i_carrier_id do not have an identical type.
    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      RAISE EXCEPTION TYPE cx_abap_invalid_value.
  4. Adjust the RAISE EXCEPTION statement for exception class CX_ABAP_AUTH_CHECK_EXCEPTION. Supply the constructor parameter textid with public constant missing_authorizations of that exception class.

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      12345
      RAISE EXCEPTION TYPE cx_abap_auth_check_exception EXPORTING textid = cx_abap_auth_check_exception=>missing_authorization.
  5. Activate and test your code. Adjust the value of constant c_carrier_id to provoke the different exceptions.

    1. Press Ctrl + F3 to activate the code.

    2. Press F9 to execute the code as console app.

    3. Set the value of constant c_carrier_id to XX to provoke the exception for invalid value.

    4. Set the value of constant c_carrier_id to UA to provoke the exception for missing authorization.

Log in to track your progress & complete quizzes