Several months back I posted my BSP Developer’s Journal Part XVI – Using the BTF Editor. This weblog focused on the BTF editor and its use, particularly within the BSP area. Then last week by request, I revisited the BTF subject by providing an example of BSP: BTF editor example, Non Model View Controller. A question came from this weblog posting about how to save the content that is created in the BTF Editor. That is the topic that I would like to discuss today.
The example use of BTF that I want to talk about today is basically using it as a replacement for the traditional R/3 Long Text Object. At my company I have built table structures and configuration that mirror the setup of the R/3 Long Text, but designed instead for holding BTF content. Similar to the R/3 Function Group STXD, I have created an ABAP Class for easy access to and manipulation of these BTF texts. Now although my weblogs about BTF in the past have focused on BTF in BSP, I want to point out that this solution can be used in either BSP applications or standard ABAP transactions.
The BTF objects provided by SAP don’t provide any real means for storing the resulting content in relation to the business objects they are connected to. This give the BTF technology considerable flexibility, but also means that we need to create structures for storing this content ourselves. Just like in R/3 Long Text, I am going to start by categorizing my text objects by a Business Object and a Text ID. I wanted to create my own set of configuration tables with different values from the R/3 ones. To do this I first created Domains and Data Elements for my Object, Text ID, and their descriptions. I then created language independent configuration tables for each element. The following are what those tables look like:
I guess I am making an assumption that my readers are already aware of how to create language independent configuration tables since I am not going into great detail on this topic. I will include this screen shot that details the special foreign key relationship that exists between the ID only table and the ID + Description table.
Last but not least, we need a place to store the text objects themselves. I created a simple transparent table, once again modeled off the R/3 Long text tables. For keys we have our application object, text ID, language, and a business key (named BTF_NAME). For instance this business key might be the concatenation of Purchase Order Number and Purchase Order Line Item. The main difference between my table and the R/3 one is that I am going to store my text as a single binary string in the database.
Now that we are done talking about the boring old database stuff, we can move on to the much more exciting technologies aspects. We will start off by looking at my exception class. If you need a nice tutorial on class based exceptions, I suggest that you check out the following weblog series by Thomas Weiss:
- The New Class-Based Exception Handling in ABAP – Part 1
- The New Class-Based Exception Handling in ABAP – Part 2
- The New Class-Based Exception Handling in ABAP – Part 3
I chose to go with an exception class that was Message Class based. Therefore when I first generated my class I received the IF_T100_MESSAGE interface. This allows me to link my exception tests directly to my message class. The following is a screen shot of my message class with all the message that I will eventually link to my exception class.
Exception Class Attributes
Now I know that I am going to have a few values that I will want to dynamically pass into my exception messages. Therefore I need to define public attributes in my Exception Class for these values. I ended up with attributes for Object, Text ID, and Object Name.
Next up we will create the individual text IDs. This is where the Exception class has a special Texts tab. From here we can create new Exception IDs, link them to a message class object, and assign exception class attributes to the variables in our message.
Sample Code for raising our Exception
Well this is getting a little ahead of ourselves, but I thought it would be appropriate to show a little sample code for raising one of these exceptions. Later in the coding of the main class itself, you will see plenty of these examples.
You can see that we are able to fill the public attributes of our exception class during the raise exception itself. We are also able to specify which text id we want to trigger because each one can be identified via a generated public constant in the exception class.
Writing SQL code is so 2004. Therefore I decided to code my database access using an ABAP Persistent Class. If you want a little background on Persistent Classes, have a look at ABAP Persistent Classes: Coding without SQL. ABAP has a nice graphical persistent class generator. You get a visual view of the source database table. You then choose the fields that you want to expose through the persistent class. When you activate the persistent class, get and set methods are generated for each field in the chosen database table. It also generates an Actor and Base Class. The following is a screen shot of the Persistent Class Builder:
We are ready to start looking at the Framework class itself. If you look at the next screen shot, you can see that this is fairly basic application class. However I did add the IF_SERIALIZABLE_OBJECT interface to this class. You will see later that we will have static methods for this class to help with serialization/deserialization of itself. This can be helpful if would want to use this class in a stateless BSP application as show in WebService Navigator Page for ABAP and Java.
The attributes of our class are all Private but one – the BTF Document Object itself (which is necessary to a pass a reference of the document object into the BTF Editor). I do have an editor object attribute for the SAPGui. This is really only included for a couple of example/test methods I will have in my class. Because the BTF Editor itself is implemented differently depending upon if you are using SAPGui or BSP, I decided to leave all this functionality up to the consuming application.
The following is a listing of all the methods implemented in this class. I hope to have exposed all the methods one would need in working with BTF. I have reading, saving, deleting, etc. methods. In addition I have a few methods for testing and example, such as the download, display in IE, and display in Editor methods.
In our constructor we receive and validate our text objects. If everything checks out we will also initialize the BTF Document object.
Since all the object keys are stored as private attributes of our class, we need a public method that will give us read only access to these keys. That is exactly what this method is for.
This is a static helper class. If a consuming application doesn’t want to use the default character encoding of Unicode (utf-8), then they should probably use the encoding that matches the logon language. This method can be called to lookup this proper encoding for you.
This method contains the code to get a reference to the BTF Main class and use it create a reference to a BTF Document object.
If your application wants to be able to set an initial string into an empty BTF document, this is the method it could use. You can pass the initial text in as a plain string and this method will take care of casting it into the binary string.
This critical method will use the persistent object to read the BTF Content from the database. It then decompresses the content and passes it to the BTF Document Object.
Equally important is our SAVE_TEXT method. This method will retrieve the BTF Content from the Document Object, compress it, and then use the persistent object to write it back into the database. Please note that an application object that uses this method, must follow this call with a commit work for the data base updates to actually be performed.
Obviously we need a way to create new text objects as well. Here we will validate that the business object name is valid, then initialize the BTF document, and finally create a new persistent object instance.
To complete our quartet of obvious methods, we have the ever popular method to delete a text object. The code here is really quite simple because we use the persistent object to delete the database entries. The same rule about Commit Work from the Save method applies here as well.
Now we come to the serialization/deserialization static methods. This helper method takes in an instance of the same class and serializes it XML. Like we said before this provides interesting persistence options for stateless BSP applications. Now earlier I added the IF_SERIALIZABLE_OBJECT interface to my persistent class as well. Therefore this inner object reference will be serialized/deserialized as well.
Just the opposite as the Serialize class. However the BTF objects didn’t have the IF_SERIALIZABLE_OBJECT interface so their object instance was lost. Therefore we will restore them in this method with a little bit of code.
Now we come to a set of methods that aren’t really designed to be consumed by a calling application. There are really only here for testing and to serve as example code (consequently, they don’t do quite as good a job of error handling as the rest of the class). Also because these methods interact with the User’s Front end through the SAPGui framework, you would never be able to call these from BSP. This first one will download the content of the BTF Document to your front end.
This method downloads the BTF and starts up your browser for viewing it.
This method will load the current BTF Document content into a SAPGui based BTF editor. This editor will open in a control based dialog box.
Our final method serves as an event handler for the CLOSE event of the CL_GUI_DIALOGBOX_CONTAINER class. If you are interested in how to code Control Framework event handlers in Global ABAP Object classes, I suggest ABAP OO mixed with Classic Dynpro
The ABAP Unit Class
The other day it was suggested that I should look into the ABAP Unit functionality for Unit Testing. As I was looking over this class in preparation for writing this weblog, I thought it might be the perfect opportunity to write my first ABAP Unit Class. It took me about 3 and half hours to create this Unit testing class. However a good portion of that time was spent on research. I read the online help and several weblogs on SDN that I found by searching on “ABAP Unit”. I also did a good amount of trial and error coding. The end result is that I ended up with 4 test methods. The first one tests the proper initialization of the class including reading/writing content to/from the database with the persistent object. The second method tests my static method for loading encoding type by language. The third method tests the serialization/deserialization functionality. And the final method tests create and delete.
The ABAP Unit Class Definition
If you aren’t familiar with ABAP Unit, this is simply another type of ABAP Object Class. It is declared as a local class in the object that you want to test. Therefore this is a local class, lcl_my_unit_test, housed in my global class, ZCL_ES_BTF_FRAMEWORK. The following is the definition of my test class.
The ABAP Unit Class Implementation
This is where the little bit of test data that I first setup in the weblog comes in handy. We will setup test conditions for a small amount of data that I preloaded into the development system. Since Unit Tests are only designed to run in a development system, we can predict that this data will be there. You will definitely want to have a look at the class cl_aunit_assert. This class contains all the comparison methods that really make up ABAP Unit. The best advice I can give you for writing an ABAP Unit class is to just have at it. I learned tons by making mistakes while trying to create this class. I’m sure that my ABAP Unit classes will get better as I write more of them.
The ABAP Unit Use
Executing the ABAP Unit test is really quite simple. It can be ran from the test options in SE80 or as part of the Code Inspector (transaction SCI). The following shows one menu path to reach the Unit Test.
If your unit test passes, you receive a status message to that effect. However I wanted to demonstrate the screens you see when a test condition fails. That turned out to be not that difficult. The first time I executed my Unit Test after adding it to my class, I actually failed on a valid condition I had never caught before. The following are some screen shots of the test failure screens.
For those of you who are interested in putting the BTF technology to work, I hope this weblog will serve as one more example to learn from. Even if you aren’t interested in BTF, I hope that you took away some learnings on persistent objects or maybe even ABAP Unit.