Exception class can be defined using to use static Text as well as the messages from the Message repository maintained using transaction SE91. In this article, lets see how to use the message from Message repository T100.
Define Exception Class using IF_T100_MESSAGE
To achieve and use the messages from the message repository, you need to include the interface IF_T100_MESSAGE
in the exception class. This interface allows you to declare and pass the message parameters when raising the exception.
As soon as you add the interface, the parameters of the method CONSTRUCTOR would be adjusted. The TEXTID field is not more referring to the field TEXTID. It would now refer to the type of the field IF_T100_MESSAGE=>T100KEY
. So, when you raise an exception, you can simply populate the fields of the key and they would be propagated back to the place where exception would be caught.
Interface IF_T100_MESSAGE has attribute T100KEY which would be available at time of catching the exception to retrieve the passed message id, message number and message parameters when exception was raised.
Exception ID in the Exception Class
For each Exception ID, in the Exception class, you can assign message class, message number and 4 attributes. To be able to assign one these 4 attributes, you would need to create a public attribute in the exception class. Once the attribute is available, you can select that instance attribute in the exception ID message text. E.g. As shown in the image, I have created an attribute IV_FIELD1 which as an attribute in the class which I have assigned to the attribute when I set the message text for the exception ID.
This text ID would be created as a Constant in the Exception Class as well.
Exception ID Constant
constants: begin of ZCX_MSG_T100, msgid type symsgid value '00', msgno type symsgno value '398', attr1 type scx_attrname value 'IV_FIELD1', attr2 type scx_attrname value ", attr3 type scx_attrname value ", attr4 type scx_attrname value ", end of ZCX_MSG_T100 .
The added public attribute, which you have used is also available as one of the parameter in the CONSTRUCTOR. This would let you assign a specific value to that attribute while raising the exception. Thus you can propagate that value
back the catcher of the exception.
Complete Public section of the Exception Class looks like this:
Exception Class Public section
class ZCX_MSG_T100 definition public inheriting from CX_NO_CHECK final create public . *"* public components of class ZCX_MSG_T100 *"* do not include other source files here!!! public section. interfaces IF_T100_MESSAGE . constants: begin of ZCX_MSG_T100, msgid type symsgid value '00', msgno type symsgno value '398', attr1 type scx_attrname value 'IV_FIELD1', attr2 type scx_attrname value ", attr3 type scx_attrname value ", attr4 type scx_attrname value ", end of ZCX_MSG_T100 . data IV_FIELD1 type CHAR10 . methods CONSTRUCTOR importing !TEXTID like IF_T100_MESSAGE=>T100KEY optional !PREVIOUS like PREVIOUS optional !IV_FIELD1 type CHAR10 optional .
Raising & Catching Exception
While raising the exception, you can provide all the parameters which are available in the CONSTRUCTOR method. This would than set proper attributes, which would be available at time of catching the exception. When proper parameters are set, it would provide proper information.
Demo
A small Demo to see it in action:
Program to Raise and Catch exception
*&---------------------------------------------------------------------* *& Purpose - Object Oriented Implementation for a Report *& Author - Naimesh Patel *& URL - http://zevolving.com/?p=2040 *&---------------------------------------------------------------------* REPORT ZTEST_NP_T100_EXCEPTION. * DATA: lo_exc TYPE REF TO zcx_msg_t100. DATA: ls_t100_key TYPE scx_t100key. * START-OF-SELECTION. * no message id, would take default ID * No additional parameter, message would be empty TRY. RAISE EXCEPTION TYPE zcx_msg_t100. CATCH zcx_msg_t100 INTO lo_exc. MESSAGE lo_exc TYPE 'I'. ENDTRY. * * Specific message ID, with parameter set TRY. RAISE EXCEPTION TYPE zcx_msg_t100 EXPORTING textid = zcx_msg_t100=>ZCX_MSG_T100 IV_FIELD1 = 'Value1'. CATCH zcx_msg_t100 INTO lo_exc. MESSAGE lo_exc TYPE 'I'. ENDTRY. * * Raising with a different message TRY. ls_t100_key-msgid = '00'. ls_t100_key-msgno = '443'. RAISE EXCEPTION TYPE zcx_msg_t100 EXPORTING textid = ls_t100_key. CATCH zcx_msg_t100 INTO lo_exc. MESSAGE lo_exc TYPE 'I'. * getting all the attributes of the message * like ID, class, attr1 etc MESSAGE ID lo_exc->IF_T100_MESSAGE~T100KEY-msgid TYPE 'I' NUMBER lo_exc->IF_T100_MESSAGE~T100KEY-msgno WITH lo_exc->IF_T100_MESSAGE~T100KEY-ATTR1. ENDTRY.
Multiple Exception IDs – Word of Caution
You would think this is great way to reduce the number of Exception Classes by creating many different exception IDs. When you have many exception IDs, they would need many different attributes. These attributes than would be added to CONSTRUCTOR. If you keep on adding various different attributes which are used in your Exception IDs, your Constructor method signature would grow. Over the time, it would be difficult for Other developers to know which fields are only needed when correctly raising the exception. Thus exception IDs should be logically grouped into Exception Classes.
Hi friends,
I appreciate the many blogs on proper obect oriented ABAP programming. For most things regarding messages, the best thing to do is:
Where ever an error is detected, a MESSAGE using addtion INTO should be present in the code. In the class and/or interface used or in the actual method, we should have an attribute, say lv_message TYPE string.
Then
TRY
something
CATCH exception
MESSAGE TYPE ‘E’ ID xx NUMBER nnn WITH aaa bbb ccc ddd INTO lv_message.
RAISE EXCEPTION TYPE Zxxx…
In the Exception constructor, make use of system variables SY-MSGID, SY-MSGTY, SY-MSGNO, SY-MSGV1 .. SY-MSGV4.
Whenever you write a MESSAGE .. INTO, you have it in a where-used-list. So you can find the code where the error occurred quickly. Did you notice the numerous SAP code lines where they to
IF 1 = 2. Message … ENDIF. Since we have MESSAGE INTO, we just don’t need this. And in debugger, we can check the full message in variable lv_message – no need to check it in message class.
Conclusion:
– Never use message class and number values except in MESSAGE … INTO.
– Make use of system variables as they are filled by MESSAGE … INTO.
Best regards
Clemens
I have to say I’m in two minds about this.
One the one hand I can see it’s usage but on the other I’m not convinced this is the correct way to go with exception classes.
Exception classes are not just simply messages from the repository they are are an excapsulaton of a particular type of error.
So for example you could have an exception class with is just for lock objects – so if a table lock fails you have that exception class for that. Or you might have an exception class which deals with incorrect paramters only.
The exception class allows you to do 2 things
1. report an error
2. determine the type of error you are encountering so that you can take appropriate action without looking at the specifics of the error.
I hope I haven’t missed the point of the article.
Nath
Hello Clemens,
Agree on the fact that having message as constant or part of variable make is impossible to include part of where used list without having to do additional work like putting check 1 = 2 (where used proof).
Regarding getting the message in String makes sense as it hides the Message implementation from the caller and definitely makes debugging easier. But, it makes a bit difficult when you need to save this exception for other purpose. E.g. IDOC status reporting – When we do INTO LV_STRING we are lose the actual message number/id/variables if we don’t use SY-MSG* fields to message variables.
I have been using INTO V_MSG approach but haven’t yet tried with Exception class. I would try soon and see if I find any drawback of using that technique.
Nice to hear from you after long time!
Thanks,
Naimesh Patel
Hello Nathan,
Thanks for your comment.
The job for exception class is to let the caller know if something happens and make sure the rest of the flow is intact. When using the Static Text from the OTR for Exception ID, you don’t have option to send additional information as part of the message. No doubt, you can have attributes which are would be available in CONSTRUCTOR to help you if you need to send out additional Info. But that would add more “thinking” on caller to understand what to do with these attributes.
When using the message from T100 approach, you can provide more information as part of the Message itself. The caller has to just decide if it wants to give that message as is or want to get additional info.
Regarding the Grouping – When you have complex design, you can try to create various message IDs to cover similar error situation. Extending your example of lock object, you can have an additional message ID of why lock occurred with more info.
Regards,
Naimesh Patel
Hello Naimesh,
thank you for understanding my underlying thoughts.
In my programs I use application log functionality. SAP provides a really powerful framework for storing messages. If you want to save messages, you must use an existing log object and subobject or create it using SLG0.
But as long as you don’t need it in the database, you can still use all functions for storing and retrieving messages.
I created a minimal class that creates an application log using FUNCTION ‘BAL_LOG_CREATE’. This function returns a handle that can be used for storing, retrieving, displaying or saving messages.
The usual program flow is
– create application log object, say GO_LOG
– MESSAGE … INTO …
– method call GO_LOG->add_sy_msg( ).
method add_sy_msg makes us of the system message variables:
METHOD add_sy_msg.
DATA:
ls_msg TYPE bal_s_msg.
ls_msg-msgty = sy-msgty.
ls_msg-msgid = sy-msgid.
ls_msg-msgno = sy-msgno.
ls_msg-msgv1 = sy-msgv1.
ls_msg-msgv2 = sy-msgv2.
ls_msg-msgv3 = sy-msgv3.
ls_msg-msgv4 = sy-msgv4.
* add this message to log
CALL FUNCTION ‘BAL_LOG_MSG_ADD’
EXPORTING
i_log_handle = mv_balloghndl
i_s_msg = ls_msg
EXCEPTIONS
OTHERS = 0.
ENDMETHOD. “add_sy_msg
(Sorry I’rather format this)
The full source code for the application log class is 294 code lines including SAP generated comments. For now i have the public methods
ADD_SY_MSG instance method to add message to log
CREATE static method creates application log object
DISPLAY display all messages as popup, i.e. at end of program run.
GET_HIGHEST_MSGTY_XAEWIS returns highest mesage type occurred – very useful for decisions in program flow
GET_MSG_TABLE returns a BAPIRET2 structured table of all messages for further use, i.e. RETURN table in BAPI function.
SAVE save log in database – only for logs created with valid log object and subobject.
You can use it in OO and non-OO context, evaluate saved logs with SAP standard SLG1 and do many more useful things.
And if you do it that way, you will have to think about messaging and message handling a lot less.
This is – off topic. But I just felt encouraged.
Clemens
Application Log made easy
Hello Clemens,
Thanks Much for the utility class on Application Log. I do use Application Log whenever I get a chance. Application Log FMs have everything wrapped which is needed for nice, clean, fast message handling. Your Utility class method to get the messages information from SY-MSG* comes very handy when using the MESSAGE .. INTO addition and still want to store the Application Log.
And your code looks nice using the ABAP Code Syntaxer 🙂
Regards,
Naimesh Patel
Hi Naimesh
Thank you for the clarification.
Nath
Hello,
I have a question related to this. It’s clear that we should use excepetion classes in case of all unexpected programm situations.
But what do you use if you just want to send some kind of information like ‘Document posted’. This is clearly no exception. I think in this case you can use the T100 messages. But official ABAP programmimg guidelines says, you should only use messages for classical dynpro programming.
I’m just curious how you deal with all those information messages. Do you use eception classes for them as well or do you use still the T100 messages.
Best regards,
Tapio
Hello Tapio,
I faced similar dilemma on what to use when to send Success message. To do that, I send the return value back and let the actor (caller) decide what to do. E.g. my UPDATE_DOCUMENT would have RV_SUCCESS. This would contain the flag when transaction is successful. I know this might not be the best elegant solution but I didn’t want to use Exception class to raise the Success message. As after Success, Caller may want to call different methods for post processing:
Method definition
Actor code would be:
Actor
Do you do similar way or you use something else?
Thanks,
Naimesh Patel
Hello Naimesh,
to be honest I use a mixture of everything. Depends on the situation. But I’m not very happy with it. I feel uncomfortable using exception classes for success messages.
Best regards,
Tapio
Hi Clemens,
Could you share your class through other means ? saplink ? else ?
if not, never mind 🙂
@One Plume
You can also use Interface IF_RECA_MESSAGE_LIST. It provides also the above mentioned functionality.
You get the instance with CF_RECA_MESSAGE_LIST=>CREATE.
Happy Logging 🙂
Tapio