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.