ABAP News for Release 7.50 – Converting Messages into Exceptions
Messages are basically short texts stored in database table T100. What makes them special is the ABAP statement MESSAGE. This statement sends a message with a short text from T100 and adds a message type (S, I, W, E, A, X). The system behavior after sending a message is extremely context dependent and I’m not really confident that the documentation covers all the possible situations.
Historically, messages were invented for the PAI-handling of classical dynpros. There they can be used to conduct an error dialog. As a rule, messages should be restricted to that usage. But there is an important exception. Messages are also closely connected to exception handling:
- In exception classes that implement IF_T100_MESSAGE, messages from T100 can serve as exception texts. Then they are part of the semantical properties of an exception class besides the class name and its super classes.
- For non-class-based exceptions, messages can play the role of a poor man’s exception text concept.
- By raising a classical exception with MESSAGE RASING instead of RAISE, you add the message text and type to the exception. After handling such a classical exception with the EXCEPTIONS addition of the CALL statement, you find the information in the well known system fields sy-msg... .
- You can catch messages sent with MESSAGE naming the predefined classical exception error_message behind EXCEPTIONS of the CALL statement.
What’s missing?
Nowadays you work with class based exceptions in your application programs. But from time to time you have to call legacy procedures that throw classical exceptions that are bound to messages. If you cannot handle the reason of the exception in place, you want to pass it to your caller in form of a class based exception. The problem is, how to find an appropriate exception class and how to convert the message based exception text of the original exception to an exception text of the exception class?
Since the exception texts of an exception class are part of their semantics, you would need an own exception class or at least exception text for each message that might occurr. Then you can raise the class based exception e.g. as follows:
meth( EXCEPTIONS exception = 4 ).
IF sy-subrc = 4.
RAISE EXCEPTION TYPE cx_demo_t100
EXPORTING
textid = cx_demo_t100=>demo
text1 = CONV #( sy-msgv1 )
text2 = CONV #( sy-msgv2 )
text3 = CONV #( sy-msgv3 )
text4 = CONV #( sy-msgv4 ).
ENDIF.
Here, meth is a method that raises a classical exception exception with MESSAGE RAISING and cx_demo_t100 implements IF_T100_MESSAGE and denotes a message that fits to the message passed by the classical exception. If there is no approptiate exception class at hand that is able to cover all the messages that might be send by a called procedure, shrewd developers proceed also as follows:
meth( EXCEPTIONS exception = 4 ).
IF sy-subrc = 4.
RAISE EXCEPTION TYPE cx_demo_t100
EXPORTING
textid = VALUE scx_t100key( msgid = sy-msgid
msgno = sy-msgno
attr1 = ‘TEXT1’
attr2 = ‘TEXT2’
attr3 = ‘TEXT3’
attr4 = ‘TEXT4’ )
text1 = CONV #( sy-msgv1 )
text2 = CONV #( sy-msgv2 )
text3 = CONV #( sy-msgv3 )
text4 = CONV #( sy-msgv4 ).
ENDIF.
This exploits the fact, that you can pass any structure of type scx_t100key to the contructor of an exception class. By doing so, you define a message from T100 not statically as message text but when raising the exception. Only the attributes for the replacement texts have to be there. Knowing that, you can create a kind of generic exception class for messages. But this is not recommended for exception classes implementing IF_T100_MESSAGE. For such an exception class, the exception text should not be dynamic and you should pass constants of that class to the parameter textid only. Furthermore, the above coding is quiet cumbersome. And further-furthermore, there’s no way to pass the message type.
Solution with ABAP 7.50
Since the above scenario is a valid use case, a solution is provided with ABAP 7.50: A new interface IF_T100_DYN_MSG that contains IF_T100_MESSAGE It adds an attribute msgty for the message type and it also adds predefined attributes msgv1 to msgv4 for the replacment texts (placeholders) of a message.
If an exception class cx_demo_dyn_t100 implements IF_T100_DYN_MSG, you can profit from a new MESSAGE addition to the RAISE EXCEPTION statement:
meth( EXCEPTIONS exception = 4 ).
IF sy-subrc = 4.
RAISE EXCEPTION TYPE cx_demo_dyn_t100
MESSAGE ID sy-msgid
TYPE sy-msgty
NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
This does basically the same as the example above, but now in a well-educated way. You can pass the full signature of a message to an exception class including the message type and the runtime environment does the rest for you. Also, you don’t have to care about the names of the attributes for the placeholders any more. When handling the exception you have access to the message, e.g. as follows:
CATCH cx_demo_dyn_t100 INTO DATA(oref).
cl_demo_output=>display(
|Caught exception:\n\n| &&
|”{ oref->get_text( ) }” of type { oref->msgty }| ).
You get back the message text and, that’s new, also the message type. From now on, this is the recommended way of converting classical messages to exceptions.
For more information and more examples see:
- System Interface IF_T100_DYN_MSG for Messages
- Exception Classes for Messages
- RAISE EXCEPTION – MESSAGE
The MESSAGE addition is also available for THROW in conditional expressions, of course.
Cool features.
Hello Horst,
just a question: The above statement handles now very well the conversion of classic exceptions to class based exceptions. What about the opposite direction?
There is already the syntax
MESSAGE class_based_exception_object TYPE 'E' RAISING class_exception.
to throw a classic exception using a class based exception because it implements the IF_MESSAGE interface. In fact the MESSAGE statement accepts any object that implement IF_MESSAGE.
So when we now have IF_T100_DYN_MSG that also knows the message type, the message statement should support the following:
MESSAGE class_based_exception_object RAISING class_exception.
The TYPE should be optional when the provided class implements IF_T100_DYN_MSG.
Regards,
Ralf
PS: Yes we also need the opposite direction as old FMs sometime are changed to use internal new OO API, but they are kept, because not 100% clear that they can be removed. Even the old code can't stay as this requires double maintenance and sometimes solving issues is much more simple using the new OO API.
As long as it is not optional, you can write
MESSAGE oref TYPE oref->msgty.
You propose a short form for that.
You are right, it is simply to get out some redundant overhead. And when we talk about overhead ... why not having a form like this:
RAISE EXCEPTION TYPE cx_demo_dyn_t100
SYSTEM-MESSAGE.
Hi Horst,
just another "always pain" thing when raising exceptions.
Today I have to code (in 7.40 before even more lines required):
DATA(ex) = cx_my_exception=>create_for_object( obj ).
RAISE EXCEPTION ex.
simply due to the fact that the RAISE EXCEPTION statement doesn't allow an expression that returns an exception object.
I like to use this way to fill the text attributes of the exception from the given object, or maybe even create objects of different sub classes.
This would not be necessary when I can use object attributes in the text constants of exceptions. In fact I can do this by hand, but in the dialog for assigning an objects attribute to the message attribute 1 to 4 I can only use the attributes of this object, not the attributes of objects that are attributes in this class. And as we have no default "toString" behavior for objects it makes no sense to pass an object to a message attribute. Maybe we simply could extend the CL_MESSAGE_HELPER->SET_SINGLE_MSG_VAR so that it is able to work with objects as well. Maybe simply by defining that in case an object attribute is used it must implement the IF_MESSAGE interface and thus will provide a "string representation" via IF_MESSAGE~GET_TEXT.
Regards,
Ralf
Hi Horst,
I am currently working on an ABAP 740 SP12 system where i can find the interface IF_T100_DYN_MSG. But unfortunately the the MESSAGE addition for RAISE EXCEPTION is not available. 😐
In the ABAP740 documentation i don't see any mention of SP12, although in ABAP750 docu i can see 740-SP10 mentioned. So what exactly is 740-SP12? 😕
BR,
Suhas
Let's have a look at the figure in ABAP News for Release 7.50 - What is ABAP 7.50?
7.40, SP12 is an SPnn after 7.40, SP08. No development from SP08 on any more (one exception: release of the CDS access control for SADL with SP10, but in fact the real release for that is 7.50 too).
Now, why is IF_T100_DYN_MSG available? It is undocumented and incomplete. Comparing the 7.40 version with 7.50 shows that the MSGV attributes are missing. Let's assume its simply a placeholder for things to come (reservation of the name). -> Forget it in 7.40!
😛
Just looked into the post now (we do not have 7.50 yet), but I also think Ralf Duckstein's suggestion of a short form
RAISE EXCEPTION TYPE cx_demo_dyn_t100
SYSTEM-MESSAGE.
should be implemented, at least as an optional short form, to get rid of 3 lines of overhead.
Mind you, in the case that the system fields (sy-msg...) were not filled by a called function (or method), but by your current piece of coding, like this:
MESSAGE E074(xx) INTO dummy.
RAISE EXCEPTION TYPE ...
MESSAGE ID sy-msgid
TYPE sy-msgty
...
then of course you can simply shorten to
RAISE EXCEPTION TYPE ...
MESSAGE E074(xx).
I assume this statement would also be found by the used-by function (German: Verwendungsnachweis)? (We don't have 7.50 yet, so I can not try it out.)
Hello Horst,
Thanks for sharing this new feature. It will be really helpful for easily passing T100 message to exception class. I have couple of points here-
1. E.g. I have created a method where I have return table of type bapiret2_t which is a BAPI return table type. When I catch an exception inside the method for some code which raises a class based exception, I get the message text back but I find it difficult to fill the bapi return structure which I am returning from the method. Now with this new feature, I can find message type with 4 message place holders. But do I get the message class and number also? Or to be clear do you have any suggestion how to populate BAPI return structure parameters after catching a class based exception.
2. I checked your more information reference and probably found a typo there in the help documentation with the following link.
ABAP Keyword Documentation
Click on the link "System Interface IF_T100_DYN_MSG in a Global Exception Class" under example section,and a new page will open. In the description section 2nd line, it should be from sy-msgv1 to sy-msgv4 instead of sy-msgv1 to sy-msgv2.
Thanks,
Mainak
1. You may use the helper method cl_message_helper=>set_msg_vars_for_any (then call FM BALW_BAPIRETURN_GET2 or something similar).
2. Corrected in 7.51, SP01, thx!
hi, I will just echo Edo's discreet question: will the search where-used for the message number (from tcode SE91) find it in the exception throwing statement you mentioned? :
RAISE EXCEPTION TYPE cx_demo_dyn_t100
MESSAGE ID sy-msgid
TYPE sy-msgty
NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
at the moment for this only reason I'm forced to use the statement message ... into lv_dummy_message and then use the syst fields to retrieve the text and variables.
Since the message id and number are passed as contents of data objects, they cannot be part of a where used list.
Horst
Great news!
But I still don't know why MESSAGE exception TYPE 'I' works but MESSAGE exception INTO DATA(message) is not allowed... ;(
It's a mess integrating exceptions into Application Log structure... :/
Why would you want to do that?
You can access the exception text directly with
exception->get_text( )
No need for involving the MESSAGE statement for that.
Oh yes of course! to have SY-message fields filled properly. For further logging (application log). Only having the text in the log is often not helpful.
Hi Enno,
you should log the exception object via the FM BAL_LOG_EXCEPTION_ADD.
it will save the message with the reference to the sy-msgid and sy-msgno. Thus you assure the translation and where used for this message.
Regards
Eskender
Is it necessary to create my own exception class (in you ex. cx_demo_dyn_t100 ) now that we can use any T100 messages. Or can I just use CX_STATIC_CHECK that is the super class of a ZCX_ exception class that I create ?
You have to include IF_T100_DYN_MSG, so ...
Hi Horst,
It's my first time using the new IF_T100_DYN_MSG exception class. Consider the following code where message 090 contains a single variable placeholder "&1".
When I run the Extended Syntax Check the first statement results in an error condition because the placeholder is not populated. The Raise Exception statement does not give the same error.
I am currently working on 7.51. Is this fixed in later releases?
Having a problem, as have raised an exception in one class, which has populated IF_T100_DYN_MSG~MSGTY with the message type, then I wish to copy this forward using RAISE EXCEPTION NEW using parameter PREVIOUS, but when I then look at the exception it has dropped the value of IF_T100_DYN_MSG~MSGTY, and I imagine it would also drop the values of other IF_T100_DYN_MSG fields of MSGV1 through MSGV4. Why is this and how do you get round it?