Whenever starting a new project it is a best practice to define development guidelines. These guidelines shall govern the development process in accordance to best practices, architectural constraints etc. with the goal to deliver a solution of good quality. In theory the availability of guidelines should be sufficient achieve this goal. However in practice this is not the case as the guidelines are often neglected and you need to check the compliance of the guidelines. This can be done manually (e. g. via code reviews) but as this is extremely cumbersome you will usually go for tools that at least “support” the developer in delivering code that complies to the guidelines (not to say enforce him to do so).
Within ABAP development there are several tools available out of the box like Code Inspector and ABAP Test Cockpit or further (free) third-party tools (like ConQat) that deliver some check functionality and can be enhanced with customer/project specific checks.
Within business rule projects you have similar requirements with respect to guidelines and their automatic enforcement. So let us take a look at the situation within BRFplus. There is bad and good news when it comes to this topic:
the bad part is that there are no SAP standard checks as compared to the ones delivered in SCI/ATC for ABAP development. This is comprehensible in the context of the BRFplus as a generic framework. The BRFplus offers a check functionality for its objects but this is more or less comparable to the syntax check in ABAP and assures the consistency of the object from a technical point of view. There are some further check functionalities in BRFplus like the check for lean trace readiness but again this covers only technical constraints. As mentioned before there is also good news: BRFplus is extremely flexible and can be enhanced at many spots in a modification free manner, so BRFplus does not hinder you in implementing the checks yourself.
The following sections will explain how you can use the concept of the application exits in BRFplus and the BRFplus API to enforce your guidelines based on a sample scenario.
Let us assume that we have the following guidelines that shall be enforced in our rules project:
- For the sake of readability the text field of the BRFplus objects has to be filled
- For the sake of readability the short text field of the BRFplus objects must not be filled with any value (so that the text is displayed)
- In case that the object under investigation is a decision table, a documentation has to be entered in the corresponding section
In our sample scenario we also assume that SAP Decision Service Management is available and we have introduced a custom attribute “Email of Responsible”. The data entry into this field is optional but in case data is entered it shall have the format of an email address. This condition represents the last constraint of our scenario.
In order to enforce these guidelines we want to raise an error message whenever a BRFplus object is checked. If the BRFplus object does not fulfill the above mentioned guidelines its activation must not be possible.
The following sections will show you how to implement the above mentioned requirements. Nevertheless I have to state a disclaimer that the code for sure is not perfect e. g. at some places a proper error handling is missing. Nevertheless the code shows the basic idea of the implementation. But now let’s jump into the implementation 🙂
As mentioned before we want to make use of the application exit mechanism in BRFplus. The usage of application exits in general is described in the document BRFplus Application Exits of Thomas Albrecht so we will not discuss the mechanism in detail here. In principle an application exit is a custom class that implements the interface IF_FDT_APPLICATION_SETTINGS and allows the enhancement of the BRFplus application at specific spots e. g. when a BRFplus object is changed. At these spots the corresponding method of the interface is triggered and enables the execution of custom code. As a prerequisite the application exit class has to be assigned to the corresponding BRFplus application.
Taking a closer look at the methods of the interface we will see that there is one method called CHECK that is triggered when a BRFplus object is checked. So this method is definitely fitting for our requirements. There is also a method called ACTIVATION_VETO that is called when an activation of an object is triggered in order to prevent the activation of the object if necessary. This method seems is also a candidate for solving our requirements. The question is: do we really need to implement both methods? The answer to this question is no – as a check is implicitly triggered when the activation of an object takes place we can restrict our implementation to the CHECK method. The implementation of the class for the application exit looks like this:
The class implements the interface IF_FDT_APPLICATION_SETTINGS and in addition we need to implement a class constructor. The implementation of the class constructor contains the setting of the class attribute GV_CHECK to ABAP_TRUE. This is necessary in order to tell the invocation mechanism of the application exit which methods shall be triggered. As we only need the CHECK method the implementation of the class constructor is done.
Next we implement the check itself to ensure our guidelines. This is done in the method IF_FDT_APPLICATION_SETTINGS~CHECK:
We do not directly put the code for the checks into the method itself but we delegate to another class (ZCL_BRFPLUS_VALIDATOR) that encapsulates the relevant checks (in order to reuse them in other scenarios as we will see later).
The next step is then to implement the checks in the class ZCL_BRFPLUS_VALIDATOR. The called method CHECK_CONSTRAINTS has the following definition:
Taking a look at the signature of the method CHECK_CONSTRAINTS which is identical to the one of the CHECK method of the application exit interface we discover that we do not get the complete information about the object to be checked but only its ID and its type. We therefore have to use the BRFplus API to get the relevant information. As a consequence the method logic is split into the following steps:
- get the reference of the BRFplus object via the ID
- trigger the private check methods (one method per check)
Hence, the implementation of the method is given by:
The red box contains the call of the BRFplus factory class that gives us the reference to the BRFplus object i. e. the reference to the interface IF_FDT_ADMIN_DATA that is implemented by every BRFplus object. This interface contains methods to fetch the information of BRFplus objects that is displayed in the general section of the BRFplus workbench. The green box contains the calls of the private methods that represent the single checks.
In the next sections we will step through each private methods to get an impression what is necessary to implement the checks.
(1) Check that texts exist
This method shall check that the text field is filled for the BRFplus object that is subject to the check. This method is defined by:
As you see we hand the reference to the BRFplus object which is of type IF_FDT_ADMIN_DATA into our method. The table CT_MESSAGES is used to hand back error messages if necessary.
The implementation of the method is given by the following code snippet:
You see that all the necessary information is at hand. We have to call the GET_TEXTS method (line 138 – 141) to fetch the texts of the BRFplus object. We get two parameters back. The parameter EV_TEXT contains the text in the logon language. The parameter ETS_TEXT contains a table containing all the available texts of the object i. e. all available translations. If we are on a translation system we can use this parameter to check if the translation has been done into all required target languages. We can then do your basic check.
For the sake of the example we assure that the text has to be available in the logon language (line 143 – 154). If this is not the case we raise an error message. We consequently fill the fields of the BRFplus error table. Two fields are of specific importance: the field ID has to be filled with the ID of the BRFplus object under investigation as this will be translated by the BRFplus into the object name and type when displaying the error message. The TEXT has to contain the message text which is then displayed in the BRFplus workbench. Be aware that the relevant parameters like message ID, message class etc. are stored as public constants of the check class.
The same check procedure is applied to the table of texts which must not be empty (lines 156 – 166).
These few lines of code are sufficient to assure this first requirement of our guidelines. As you can see the effort is very low whereas the gain is definitely high as the first step for a readable rule representation is done.
(2) Check that no short texts are entered
This check shall assure that no short texts are filled in the BRFplus objects in order to enforce that the texts are displayed. From a design point of view this check is the same as the check for the texts. So is the definition of the method shown below:
The implementation of the method is given by the following code snippet:
As in the case for the texts we use the method GET_TEXTS to retrieve the short texts i. e. the table ETS_SHORT_TEXT with all available short texts. The check itself and the filling of the error table is the same as in the check for texts presented before. Here we omitted the check for the short text in the logon language.
Be aware that when using DDIC binding the short texts are automatically derived from the information available in DDIC. You can remove that information afterwards in BRFplus (and keep stuff really readable) but if you want to accept that situation for data objects with DDIC binding you can soften the check as shown in the next screenshot:
Here we now first check if the object is a data object (line 172). Afterwards we cast the object to the data object specific interface IF_FDT_DATA_OBJECT. We then check if there is a binding and in case omit the check.
(3) Check that custom attribute contains a valid email address
This check method shall enforce the entry of a valid email address in the corresponding custom attribute. The data entry into the field is optional. The definition of the method is the same as for the two previous checks and given by:
The implementation of the method is shown in the following code snippet:
The custom attribute values can be retrieved via the method GET_CUSTOM_ATTRIBUTE_VALUES of the BRFplus admin interface (lines 192-194). This method returns a table of all available custom attributes. We fetch the right attribute from the table by identifying it via its UUID that we stored in the constant MC_ATTRIBUTE_ID (line 196). We first check that the attribute is not initial (as the filling of the attribute is optional) and then we use the class CL_ABAP_REGEX to do a basic check if the attribute value matches an email address in accordance to a regular expression (lines 203 – 206). If this is not the case an error message is attached to the BRFplus error table (lines 208-216).
As in the methods before the effort to implement such a check is quite low (well … the visible coding effort; the finding of the right regex might be a different story 😉 ).
(4) Check that decision tables have a documentation
Last but not least we want to check that decision tables have documentation. The method to do so has the following definition:
This time we transfer beside the reference to the object also the object type to the method.
The implementation of the method is given by:
As we want to do the check only for objects of type “decision table” we have to do a filtering on that expression type. Naively one might expect that the parameter IV_OBJECT_TYPE already states if we have a decision table or not but this is not the case. As the decision table is an expression we first use the parameter IV_OBJECT_TYPE to make sure that the check is only executed for expressions (line 222). As we do the check only for an expression of the type “decision table” we have to cast the imported object reference afterwards to the interface IF_FDT_EXPRESSION. This way we can determine if we have a decision table or not using the instance attribute MV_EXPRESSION_TYPE_ID (line 226). We compare this attribute to the corresponding constant for a decision table that is available via the interface IF_FDT_CONSTANTS. After this filtering we use the method GET_DOCUMENTATION of the administration interface to get the documentation of the object. The semantic of the method signature is the same as in the case for the texts. We get the documentation in the system language via the parameter EV_DOCUMENTATION as well as the documentation in all available languages via the parameter ETS_DOCUMENTATION (lines 228 – 232). We perform the same logic on these parameters as we did for the texts namely we have to have the documentation available in the system language (lines 233 – 243) as well as at least one documentation in any language (245-255). if this is not the case we raise an error message.
After implementing the application exit class as well as the class containing the basic checks and assigning the application exit class to the BRFplus application, we test if we have reached our goal.
The screenshot below shows an expression that belongs to an application with the application exit class assigned. As you can see when entering a short text and executing the check an error message appears. The message shows the name of the object, its type and the text of the error message itself.
The same is the case for the other types of checks which are not shown here as screenshots (anyway I hope you believe me that this is the case)
Whenever implementing a new application we can assign the application exit class and assure our quality requirements. That is quite good news, but maybe the implementation of the rules has already started before we could put the exit in place. Or there are some old rule implementations that have already been finished. We can for sure assign the application exit class afterwards (and we also should do so) but the checks will not run if the corresponding objects are not checked or changed. So do we have to manually check all the old objects now in order to align the “legacy rules” with our new checks? The next section will give an answer to that question.
What about “legacy” code?
For sure the manual check of the BRFplus objects is cumbersome and nobody wants to do that just to find out that everything is ok. Such a manual check is indeed not necessary at all. As we did within the checks of the BRFplus design-time we can now use the BRFplus API to get all the relevant objects and execute the checks on them. As we have the checks already in place (and have encapsulated them in a separate class) the effort to implement such a report is quite low.
So for our “legacy” rules we implement a report that has as parameter the UUID of the application we want to run the checks on. The report shall display the results of the checks as an ALV for each object that has been checked. The check results shall be displayed as red (failed check) or green (passed check) traffic lights.
The selection screen of the report is shown below and allows the entering of an UUID for a BRFplus application
What do we have to do within the report? First we check that the ID belongs to an active application. This can be done using the class CL_FDT_FACTORY=>GET_ID_INFORMATION as shown below (there is a similar method of the class CL_FDT_ADMIN_DATA=>CHECK_ID whcih also does the job but this one must not be used as it is only for SAP internal usage)
After that we have to fetch all the objects belonging to the application. This can be achieved using the so called query object of BRFplus. As you see in the screenshot below in line 53 we create that object using the BRFplus factory CL_FDT_FACTORY. Next we set the selection parameters for the application (and for all objects within) we want to perform our quality check on (lines 55-60). After that we call the SELECT_DATA method of the query object (line 64-68) to give us all the UUIDs of the objects within the application (table ETA_DATA).
We loop at that table and call our check class ZCL_BRFPLUS_VALIDATOR for each UUID. The only thing left to do is to get the object type of the BRFplus objects we are running our check against. Here once again the CL_FDT_FACTORY class can help us via its method GET_ID_INFORMATION. This method returns the desired information (the method hands back a lot more information, so you should take a look at its complete signature). The code below shows these steps as part of the check report (lines 75-90).
As the report should give us some readable output (beside the UUID) we fetch the (technical) name of the BRFplus object (lines 92-96) using again the query object and put together some basic information for the display of the check results. We show the UUID and the name of the BRFplus object and in addition the results of the checks as red and green traffic lights. The basic initialization of the output structure is shown in lines 98-103. Next we have to translate the error messages into the corresponding traffic light colors which is not shown here as there are no real secrets in doing so (just a lot of IF cases).
In addition to make life a little bit more comfortable we want to allow the user to directly jump from the check result to the corresponding BRFplus object by clicking on its UUID. This is achieved by the standard ALV functionality for line selection. In the following screenshot we show the corresponding form that is triggered by a line selection of the ALV. The start of the BRFplus workbench is also no big deal. As we have the UUID of the BRFplus object we fetch the reference to the UI execution object of the BRFplus workbench using the BRFplus WebDynpro factory (CL_FDT_WD_FACTORY) as shown in line 256. Next we call the EXECUTE_WORKBENCH method exporting the UUID of the BRFplus object (lines 258-260). This starts the BRFplus workbench which opens the object belonging to the UUID.
After the execution the report will show the following result:
As you can see some checks failed. By selecting the object in the ALV we jump off into the BRFplus workbench. This is shown in the next two screenshots (do not take a too close look at the second one as you might find another inconsistency):
The result i. e. the output of the report is quite rudimentary. Using the code snippets shown above you can easily enhance the output with additional information on the single objects like text or object type.
Beside the quality check of the rules via the application exit class or a report you can also think about hooking the checks into the release process of a transport request. This can be achieved using the BAdI CTS_REQUEST_CHCK i. e. its method IF_EX_CTS_REQUEST_CHECK~CHECK_BEFORE_RELEASE. As we have encapsulated the logic of our check a separate class the procedure of hooking the check into this BAdI is comparable to the one that we have shown in the report above.
This blog hopefully gave you some ideas how you can use the application exits and the BRFplus API to perform checks ensuring your guidelines in your BRFplus projects as you would do it in your ABAP projects. The examples shown above are more or less a “finger exercises” to give you an impression what is possible (and how much code is needed to do get the relevant information about the BRFplus objects). In real life/projects the content of the checks will certainly be more sophisticated, but the basic structure will be comparable to what is shown here. So this should help you to get a kick-start in your implementation.
If you start to use this concept I also want to make you aware of a possible pitfall when implementing the check via the application exit: Within the application exit class you can get access to all objects of the application (and beyond) using the BRFplus API. Nevertheless in any case you should avoid cross checks i. e. check other objects than the one on which the application exit is triggered as this can cause deadlocks when you model your rules. If you want to do checks that have to use information from different objects in your application use a separate report or integrate the checks into the release process of the transport. Within these events you can be sure that the modeling is finished and such cross-checks make sense
The sources of the sample programs are now available as SAP Nugget attached to this blog. Please keep in mind that this is sample code to give a quick start but the code is not quality assured to be used in productive scenarios (aka disclaimer 😎 )
Due to the attachment policy you have to set the file type from .txt to .nugg after download.
The code is written in ABAP 7.40 style, so when importing into a NW 7.31 system you will have to do some rewritting of the code.