One of the problems ABAP developers face from time to time is the need to store arbitrary values for processing. These values do not always justify creating a new table, and there is no convenient place to store such values. An example would be when some logic depends on a master data value (like a certain customer), or if the developer provides some tool that provides some customization options which need to be saved in an unstructured manner.
In cases where creating separate config tables are not warranted, developers often come up with creative ways to store values to avoid hard-coding them, such as writing entries to TVARV. However, this is not a very neat solution and sometimes leads to awkward workarounds for more specific sub-cases, like inventing conventions for putting composite keys into the name.
The solution I am presenting here (see below for a link to the source) consists of a hierarchical value store, not unlike the Microsoft Windows Registry. It allows the developer to retrieve and store values that cannot neatly be associated with some configuration and which does not warrant the creation of a new table.
The store is backed by the table INDX, which you will find standard in every ABAP installation and which is accessed conveniently with the statements EXPORT TO DATABASE and IMPORT FROM DATABASE. There is of course no reason not to use a custom table for this, but using INDX avoids you having to create yet another table; although if you are worried about keys colliding with other entries in there, you can simply use search-and-replace to specify your own table.
The solution is presented in the form of an API, the source for which is contained in an include. You could of course use the source-code view in the class editor in SE24 to add the class to the repository, but distributing the code as an include is just very convenient, and I am all for convenience.
Furthermore, the values are all stored as strings, though I imagine that if you consistently read to or write from fields of the same type, that should not be a problem. Maybe in a future version, I will look at adding a type specification for each value stored.
Additionally, there is some concurrency control in the form of optimistic locking, though I am not entirely sure whether there is a practical need for it. Adding new entries (keys) to the registry causes the new entry and the parent entry to be saved immediately, while values in each entry must be saved explicitly (although they will be saved automatically when making changes to the entries; I am still thinking about auto-saving all changes).
The second part of the solution is a registry browser/editor, which is similar to the RegEdit application on Windows, which allows an administrator/consultant to inspect the contents of the registry.
(The editor, like the library, is provided as a self-contained piece of source code that can be pasted into a report program, avoiding you having to create additional items in the repository. See below for the link to the source code).
Here is an example of how you could use the registry:
* Make the registry API available to our program
DATA: reg_root TYPE REF TO lcl_registry_entry.
DATA: reg_entry TYPE REF TO lcl_registry_entry.
DATA: lv_customer TYPE kunnr.
DATA: lv_run_date TYPE d.
DATA: lv_timestamp TYPE TIMESTAMP.
* Get the root entry of the registry
reg_root = lcl_registry_entry=>get_root( ).
* If we want to ensure, on startup, that a certain entry exists, we
* could do the following (e.g. in LOAD-OF-PROGRAM):
reg_root->create_by_path( ‘Sales/Enhancements/Process_XYZ’ ).
* Retrieval of a specific entry. If we did not have the above line,
* we would have to check that the result of each call to GET_SUBENTRY( )
* to ensure it is bound.
reg_entry = reg_root->get_subentry( ‘Sales’ )->get_subentry( ‘Enhancements’ )->get_subentry( ‘Process_XYZ’ ).
* Getting a specific value from the entry:
lv_customer = reg_entry->get_value( ‘ProcessCustomer’ ).
* Writing values to the entry:
lv_run_date = sy–datum.
reg_entry->set_value( KEY = ‘LastRunDate’ VALUE = lv_run_date ).
GET TIME STAMP FIELD lv_timestamp.
reg_entry->set_value( KEY = ‘LastRunDateTime’ VALUE = lv_timestamp ).
* Saving the entry
I have given some thought to security; whereby you would want to prevent your section of the registry becoming inadvertently overwritten by another application or person. One approach is to lock down the registry and require applications to write their entries and provide an applicable UI, which is analogous to how Windows applications operate, e.g. when recording user settings. Another approach would be to extend the editor so that it can be run to access only a certain branch of the registry tree and give specific users access to that.
You can find the entire source code of the include for the library, as well as the registry editor and the example program at the following Gist on GitHub: https://gist.github.com/mydoghasworms/08ea60e95dd1fa90c90a
This article has been adapted from my original blog post here: Arbitrary value store (registry) for ABAP