In the previous weblogs of this series the focus was how to raise, delegate, and handle exceptions. This weblog gives an example how to build a user-defined exception class, how to provide one or several standard-texts for this class, and how to insert parameters in these standard-texts. These parameters are filled by actual values passed to the exception object when it is created.
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:
Building Our Own Exception Class
Now it is time to build our first exception class. Suppose we have a program which allows you to withdraw money from a savings account. When you try to draw more money than exists in the account, an exception should be thrown.
We choose to build a global (local would also be possible) exception class cx_out_of_money. We open the classbuilder (transaction SE24) or the object navigator (transaction SE80) and start to create the exception class. Using the prefix cx for the class name signals to the classbuilder that we want to build an exception class, and it then supplies us with the default settings needed for an exception class.
We choose the superclass that is proposed by the classbuilder: cx_static_check. This means that the compiler checks if the exception is declared the interface in case it is not handled locally. (I will show in another weblog why there are also two other exception superclasses. For classes derived from them the enforced declaration is less strict or rather does not exist at all.)
If we selected the checkbox "With Message Class" (only available since 6.40), we could use table T100-based texts. As a prerequisite, these texts must first be input using transaction SE91, as you cannot do this from the tab "Texts" in the classbuilder. We choose to leave the checkbox unselected which means we will maintain our texts in the Online Text Repository (OTR), which is accessible from the classbuilder.
Let me remind you again about how to use these texts: If you raise an exception, be sure to pack an exact description of the error into the text. If you handle the exception and think it necessary to output some information, always consider who is the addressee of this message. The end user will only be able to understand semantic exceptions concerning the business logic. Do not pass technical exception texts to the end user rather write them to a log file. If it is absolutely necessary to inform the end user about the technical problem, catch exceptions with texts he or she will not understand and raise new exceptions with texts on an end-user level. This is what is called mapping an exception.
Going on with building our exception class, let us skip the usual screen for assigning your class to a package. You probably already know how to do this.
The Standard Attributes of an Exception
Next you see the attributes tab:
These are the attributes inherited from "cx_root", from which all exception classes are indirectly derived:
Preparing an Exception Text
For real-life programs do not write exception texts in attributes, but rather in the tab "Texts", where the OTR enables an easy translation of its entries. But of course, this is only a corollary of the rule that you should never write texts for the end user by hard-coding them in strings, but instead provide translatable texts. We write an appropriate text for our exception in the OTR. Write the text for an exception in the line where an exception ID with the name of your class is already prepared by the classbuilder.
In our example the exception is thrown, when you try to withdraw more money than is in your savings account. The exception class should also contain some information about the actual balance in the account and the amount you wanted to withdraw. The values of attributes can be part of an exception.
First we create two new instance attributes, which hold the values we want insert in our exception text. We switch again to the tab rider "Attributes" and create the attributes "amount" and "savings".
Now let's go back to the tab rider "Texts" putting the name of the respective attribute between ampersands in the text. When the text is returned by the method get_text, the actual values of the attributes are inserted:
When inserting the text and then saving it, don't worry about the different OTR pop-up windows that open. Just confirm everything and return to the attributes tab. There is now an automatically created attribute with a reference to this OTR entry.
How to Pass Text to the Standard Exception Text at Runtime
You access the text of an exception with the method get_text( ). The content of this text is determined when the exception object is created. When you raise an exception, the text of the exception object is, by default, the text with the ID of the exception (it is also possible to create an exception object first and then raise an exception with this object, but I will not treat this here). Take a look at how we pass the actual values myamount and mysavings to the exception when it is raised.
If we run the program and set myamount to 10000, we get the error message: "Withdrawal of 10000 not possible: Savings only 5000."
Different Texts for One Exception Class
Suppose you want to use the exception class cx_out_of_money with a different message, let us say: "No money in the savings account." To do this we insert an additional line in the text tab; we name the text ID "no_money" (as with variables, choose speaking names for those IDs, rather than using IDs like "additional_text") Next we fill it with the text we want. Looking at the attributes tab you find that there is an attribute additional_text keeping a reference to the new entry in the OTR. When we create the exception we now have to pass the reference to this field to the parameter TEXTID:
IF mysavings = 0.
By the way, let me just mention a common mistake which might easily occur when you are in a hurry and not yet accustomed to the new class-based exceptions in ABAP: Though catching and handling the exception, you still get a short dump when running the program. If that happens, check that you have not coded: raise cx_out_of_money. This raises an old exception, which cannot be caught by a CATCH clause for the new object-oriented exceptions. Make sure that you have the correct code to raise an exception: raise exception type cx_out_of_money. This way you make sure that you do not mix old and new exceptions in one procedure.