Message handling in BOPF
When it comes to implementing a transactional application, I don’t believe that message handling is one of the first architectural concepts the average developer thinks about – at least I didn’t.
But when starting to implement a BOPF-based application, all the rest of the technical services (buffering, database-access, locking) are already provided and you don’t care about them. So latest when implementing the first validation, message handling is getting into the picture.
I’d like to explain the basic concepts.
When implementing a transactional application, the system often has to convey information about its processing to the user.
Traditionally, SAP developers now think of course of se91-based message classes which have been introduced looooong time back.
When it comes to handling the creation and propagation of messages, all methods which can potentially raise messages export a table of BAPIRET2.
However, this structural approach of the se91-message is quite limited: It implies max. four variables which can be transported together with the T100-key. The consumer has to determine the semantic of the content of MSGV1..4 which might differ from message to message, so the consumer has to interpret the message.
In order to overcome those shortages, there is an object oriented version of the message in the ABAP. You don’t kow it? That’s no wonder, it’s brand new – well almost… First problem is how to make the lingual differentiation: Since the se91-entity is also called “message class”, it’s quite tricky to find a term for that other kind. I use the term “class-based messages” in daily language (in correlation to the class-based exceptions which are in fact the same technology).
Similarly to class-based exceptions, class-based messages are created in the ABAP class-builder. They can use inheritance in order to form a hierarchy which defines common methods and attributes. Propagation of these objects to a centralized, mostly higher-level-component which handles the messages (writes them to the UI) can be done more easily and by casting the objects, their content can be interpreted.
You may have been using the same concept when adding T100-texts to your exception classes: Once the class implements IF_T100_MESSAGE, messages from a message class can be bound to constants. Attributes which are added to the message class are automatically generated into the constructor-signature.
I don’t know why this concept is not incredibly popular, but also I did not come across this concept until I’ve been using BOPF. When using BOPF, you have to get to know it and you’ll probably also not understand why you have not come across it yet 😉
The message container
The interfaces of all entities which can be implemented within a business object and might need to raise a message to the user contain a message container: /BOBF/IF_FRW_MESSAGE. Into this object, instances of the message classes (“message objects”) can be put.
Please note the the message container is not a handler which is injected by the framework, but it’s a container which needs to be initialized before a message can be added to it! Personally, I consider this (one of the very few) flaws in the design of BOPF and in order to get rid of the constant checking for an initial container, we decided in our project to go for a static helper method which performs this task:
All class-based messages of which instances are created at runtime need to be subclassed of /BOBF/CM_FRW. In our projects, we created one se24-class per business object which directly inherited from the framework-message-class.
If you are using an own UI-infrastructure (not FPM – which would be a pity of course), you may think of introducing a common superclass for class-based-messages from your application.
/BOBF/CM_FRW does not define any texts, but it provides a signature which allows generic processing within BOPF.
Some attributes are also filled by the framework (marked in blue. This mostly simplifies debugging, like information about the determination/action/validation which has issued the message). But there are also attributes which have to be provided by the originator of the message, such as its severity.
Most importantly, the framework-message-class has an attribute which allows to assign messages to instances for which they have been created: The so-called origin location.
The origin location
When raising messages to the user, it’s not always easy to convey the information about what has caused the message or which attributes have to be corrected in order to make the funny message disappear. Particularly generic messages are hard to understand and it’s good style to include the necessary values into the message variables. This has its limitations though and if multiple instances with similar data are on the same screen, correcting issues is sometimes trial-and-error for the user.
The origin location solves this dilemma:
The KEY of the origin location refers to the instance of the data which is addressed, while BO_KEY and NODE_KEY denote the model parts of the instance.
BO ‘ZCARRIER’ (precisely its Model-GUID)
Node ‘ROOT’ (precisely its Model-GUID)
Key ‘LH’ (precisely the GUID of instance with alternative key ‘LH’)
Optionally, the fields which have to be checked by the user can be supplied in the ATTRIBUTES table.
This allows the user interface to interpret the message generically and not only display the text, but also to highlight the instance and fields on the screen:
To me, the way BOPF handles messages is close to ideal. Messages as objects have got huge advantages to structure based messages. The common superclass enables generic handling by a message component and the information collected is very verbose.
Only flaw in my opinion is the exporting of a container instead of a message handler, but this shortage can be resolved.
What do you think? Is there anything you are missing in the way BOPF operates messages? Or how do you do that in your development?
I’d be most interested to read about that in your comments.
Finally it got published... 🙂 Oliver Jaegle
are "Class-based messages" exclusive within BOPF or ist it something general?
Best regards, Tapio
Class based messages are nothing special to BOPF. Only the common superclass which allows for common processing of the messages within the framework is special.
Feel free to introduce them into your custom application architecture as well 😉
sorry, I haven't got it completely.
Does it mean you have to build your own classes "ZCM_FRW" and "ZCL_FRW_MESSAGE" and just take over the conceptional ideas how BOPF is processing messages
does it mean you can use /BOPF/CM_FRM and /BOBF/CL_FRW_MESSAGE directly in your own applications, even if they haven't anything to do with BOPF.
Best regards, Tapio
sorry for not being verbose enough - I was writing on my smartphone this morning 😉
As you asked whether class-based message in general are something exclusive in BOPF I assumed you thought of using the same concept in your custom development for propagating messages. This basically meant to build your own classes "ZCM_FRW" which provide a common handling of message objects within your application.
Using the /BOBF/CM_FRW as superclass in custom non-BOPF-development has only a very few benefits while coming with some overhead (e. g. the GUIDs in the origin location). Therefore, I would not do this but create a custom superclass.
As for the message container (/BOBF/CL_FRW_MESSAGE ): If it was up to me, I'd use a custom handler object instead of the container which is injected into the interfaces which process the data. This allows for easier mocking and is more comfortable to program to, as the one creating the message objects does not have to care about the instantiation of the container.
Hope this clarified your question - it would be so much easier to do this on a white board 😉
yes, now it's more clear to me. Thank you very much!
Best regards, Tapio
Thanks for this clear blog. I have a question about saving a class-based message in a table.
How would you save a class-based message in a table when you don't want to save the text message, but the message meta-data to be language independent. Like if you save a message of a message-class, then you would save the structure of type BAPIRET2.
when it comes to persisting those message objects, various methods of different complexity come to my mind.
1) Extracting the message variables
If it's just about persisting the language-independent message text, all that needs to be done is mapping the members to message variables and persisting them together with the T100-Key. The result can be of type BAPIRET2. How to find the proper member for each of the four potential message-variables can be copied from the get_text-method 😉
However, I don't quite like this approach as information about the message which is not part of the short-text gets lost (e. g. additional members which can be "printed" into the long text.
2) Persisting the message object
As we've created an instance implementing IF_MESSAGE, but don't, know about the final typing, the challenge is to capture the members. I can think of three ways doing that (although I have only used one of those implementations yet).
2a) Implement a data mapper. Use a custom ABAP class to which the message object is passed. The persistence could consist of two tables: One capturing the common attributes of all message classes to serialize (e. g. the origin location, the T100-Key, severity, ...) into a structure. The additional members of the message object could be analyzed using RTTI (class-descriptor) and could be persisted in a related name-value-table.
2b) Use a simple-transformation (e. g. the ID-transformation) transforming the whole object to XML and perisst this string. In addition to the serializted object, also common attributes could be stores as structure in the same table in order to find the message-instances more easily.
2c) Use the ABAP object services for serializing the message. I have not done this and don't know how this can technically be achieved, but as the message object is an object, it should do. And performance is usually not the key requirement for accessing messages
I prefer 2b) due to the flexibility, ease of implementation and performance.
What do you think?
Thanks for the clear answer. I agree that solution 2b is the best option.
This was also the solution I came up with, but there are two things that are holding me back:
1: The amount of data that is stored in the table for a message.
2: We have to retrieve all the messages from the table and put them in for a message container to determine for example only the errors.
Thanks and hope to see you again next d-code (Teched).
With respect to storage you are of course right. But that's a common issue: if you want more information, it consumes more space on disk. Also, disk storage is not what worries me at all in the age of petabytes.
As per your second remark: searching in serialized objects in a relational database does not perform well. Therefore I suggested to store the attributes relevant for search redundantly as attributes of the row containing the serialized message-object. This is what I meant when writing "In addition to the serialized object, also common attributes could be stored as structure in the same table in order to find the message-instances more easily".
"My" DB table looks as follows:
Does that answer your (second) question?
Thanks, yes that answers my question.
When I read you comment on my question the second time, I saw that you already mentioned the 'In addition...', sorry I didn't noticed it the first time.
Thanks again, very helpful.
sorry, but I have to come back to this again. I'm now in the process to build my own class-based message framework. I still have a question regarding the concept.
You wrote "Only flaw in my opinion is the exporting of a container instead of a message handler, but this shortage can be resolved."
Do I understand this correct, the message container is just a table which contains all the the class-based messages which are collected by the application? On the other hand a message handler is storing all the class-based messages and in addition it provides also some logic for treatment those messages, e.g. storing the messages in an application log or write message(s) to UI?
I guessed that this aspect of my blog might be a bit confusing, also as I am not too sure whether my terminology is correct. Let me try to explain:
The "message container" I was referring to is an object (implementing /BOBF/IF_FRW_MESSAGE) which also provides some logic for treating them (e. g. copying a message to a new location and similar). The difference towards what I was referring to as a "handler" is only the way the framework communicates the object to the implementation: I know a "handler" to be injected by the framework (meaning the object instantiation would we be done by the framework, the implementation could always rely on the handler to be available). A handler would be available as an IMPORTING parameter for e. g. a validation.
The message object however needs to be actively created by the implementation. It is an EXPORTING parameter. Thus, the implementation (e. g. the validation) also needs to ensure that the object always has been instantiated before interacting with it (e. g. attaching a message to it). I call this a "container" in my daily language, as it actively needs to be retrieved before being able to fill it.
Personally, I consider a handler to be more elegant to interact with, particularly with respect to the reduced responsibility of the implementation.
Could I make this aspect more clear?
ok, thanks for your information. This is now more clear to me.
Excellent job. Does this mean for BOPF applications like TM, the old way of customizing message severity using Table T100S will no longer work.
Is there some other way to configure message severity (without code change).