The upcoming release of kbmMW will also contain a brand new configuration framework.
What is a configuration framework?
You have probably already used one many times, in the form of reading or writing data to/from INI files, registry settings, your own XML files etc.
Basically most applications needs to obtain some user defined configuration from somewhere.
In kbmMW we have until now been doing it the same way as everybody else, but it is so boring and tedious to handle configurations the old fashioned way.
Why do we have to write all that boiler plate code to read some data form a configuration, or to store something back into the configuration, and doing that we usually forget doing backups and such, risking to damage a working configuration, if we experience an unexpected power failure at the wrong time.
But that is all past now 🙂
kbmMW’s new TkbmMWConfiguration class and it’s accompanying storage classes descending from TkbmMWCustomConfigurationStorage will automate all the tedious work for you.
So how to use it?
The simplest way possible to read a configuration:
unit Unit1; interface uses ..., kbmMWConfiguration; procedure TYourClass.DoSomething; begin SomePath:=Config.AsString['myconfig.somepath']; Config.AsString['myconfig.somepath']:='SomeNewPath'; end;
As you can see, you will have a thread safe Config instance readily available for you, and we are using it to read some string out, and store a string back.
So where is this configuration then persisted?
It will default create and access an XML file named yourapplicationname_config.xml placed in the startup directory of your application.
It’s contents, given the above example, would look like this:
<?xml version="1.0" encoding="utf-8" ?> <config> <myconfig> <somepath>SomeNewPath</somepath> <myconfig> </config>
As you can see, it will use the dot notation of your keys to allow you to group your configuration settings anyway you want.
Obviously there are several additional methods to AsString which can be used for fetching or storing other types of data.
Currently kbmMW provides:
And some functions to easily return a default value, in case nothing is defined in the configuration file. They all follow the same syntax like this one:
function AsDefString(const APath:string; const ADefault:string; const ASetIfDefault:boolean = false):string;
Notice the ASetIfDefault value. It is default false, but if you set it to true, the configuration storage will be updated with the default value, if no value is found for the key given by APath.
The other functions are
You can test for if a value exists in the configuration file by using the contains property:
if Config.Contains['myconfig.somepath'] then...
The configuration file is automatically opened and loaded the first time you use any of the above properties/functions.
Notice the distinction between opened and loaded! If you remember the good old INI file, then you open it, and every time you read from it, you will read from the actual file, and thus immediately see changes without the need to reopen it. The same when using the Windows registry.
That may be a nice thing, or it may be wasted CPU cycles depending on the scenario. If you read a value lots of times, you would usually put the configuration into a variable that you manage, not to waste too much energy reading it from the INI/Registry.
How does kbmMW do it? It depends on which of the configuration storage’s that is being used.
kbmMW currently supports the following storage’s:
The Ini and Registry configuration storage operates live on the INI file and the Registry entries, as you would normally expect.
The remaining configuration storage’s operates on an internal representation of the configuration, which will be streamed back into the file at relevant times.
What is a relevant time?
- Upon application shutdown if anything has been changed
- Upon calling Save method on the storage if anything has been changed
- On every change, if AutoSave property is true
If you use the XML, YAML or JSON storage, you will even have the advantage of kbmMW automatically backing up your old configuration before saving the new.
You can control how many backup files you want to leave via the BackupMaxCount (default 5) property, and where it should be backed up to via the BackupPath (default same path as original file) property and finally what extension the backup file should have via the BackupExt (default ‘bak’) property.
The XML, YAML and JSON storage methods all stores the data in a tree structure following the dotted names of the given path.
The Ini storage method use the first segment of the path as the section name, and the remaining part of the path (including dots) as the key name. Eg.
Will result in an ini file like this
The Registry storage method use the last segment of the path as the name value and the remaining as the registry key tree path. In addition one specify the RootKey (defining which registry hive to operate) and the starting place in the tree within the hive when creating an instance of the storage.
If you want to change the storage method of the configuration, you do like this before using any of the access methods:
Config.Storage:=TkbmMWRegistryStorage.Create( HKEY_LOCAL_MACHINE, 'Software/MyCompany');
With the above registry storage, setting Config.AsInt32[‘a.b.c.def’]:=10 would result in opening the registry key Software/MyCompany/a/b/c in the registry hive HKEY_LOCAL_MACHINE, and setting the value def to the integer value 10.
kbmMW automatically opens and closes registry keys as needed.
You have seen how easy it is to access the standard Config object. But what if you explicitly want two (or more) different configurations concurrently?
Solution. Instantiate your own TkbmMWConfiguration instances. Eg.
var myConfig1, myConfig2:IkbmMWConfiguration; begin myConfig1:=TkbmMWConfiguration.Create; myConfig1.Storage:=TkbmMWXMLConfigurationStorage.Create('myconfiguration1'); myConfig2:=TkbmMWConfiguration.Create; myConfig2.Storage:=TkbmMWXMLConfigurationStorage.Create('myconfiguration2'); ... end;
This way you can also specify your own naming of the configuration, which in the above examples results in myconfiguration1.xml and myconfiguration2.xml unless you also decide to change the configuration storage method.
myConfig1 and myConfig2 can then be used the same way as we used the standard Config object.
It is now already simple to access the configuration from anywhere in your application, but you can also make it automatic.
Say you have an object which you want to hold certain values based on your configuration. You could yourself instantiate the object and assign the values using the As… properties, but there is also another way.
TMyObject = class(TkbmMWConfigurableObject) private [kbmMW_Config('myconfig.somepath')] FSomePath:string; public property SomePath:string read FSomePath; end; initialization kbmMWRegisterKnownObject(TMyObject);
By descending your object from TkbmMWConfigurableObject, and using the kbmMW_Config attribute on either properties or fields, your configuration will automatically be read and optionally written when you instantiate or destroy your object.
Upon instantiating TMyObject, then the property SomePath would have the value ‘SomeNewPath’, provided this blog previous examples were followed.
Default the fields/properties with the attribute on are only read from the configuration storage. If you want the configuration to be automatically updated with new values from your object, then you need to provide an additional argument to the attribute:
You can also define if date/time values are read and stored as UTC (default) or local time values, by setting yet another argument.
kbmMW also supports FSomeTime being a regular TDateTime.
You can force the reload of the configured properties by calling the
ReadConfig method of your configuration object, and you can force saving the properties that supports being saved by calling WriteConfig.
You can switch which configuration to load the configuration from by setting the Configuration property of your object to point to a relevant kbmMW configuration instance and explicitly call ReadConfig.
What if you already have an object hierarchy and it is not possible for you to inherit from TkbmMWConfigurableObject? Then you can still use kbmMW’s smart configuration by manually requesting the configuration and registering your object as a known object with kbmMW. Eg. now not descending from TkbmMWConfigurableObject:
TMyObject = class private [kbmMW_Config('myconfig.somepath')] FSomePath:string; public property SomePath:string read FSomePath; end; initialization kbmMWRegisterKnownObject(TMyObject);
To load the configuration do:
and saving it
If you want to make it automatic for your own object, override the methods AfterConstruction and BeforeDestruction of your object class.
... public procedure AfterConstruction; override; procedure BeforeDestruction; override; ...
And call the above ReadConfig and WriteConfig calls.
Finally, if you use kbmMW smart services, you can also automatically use kbmMW configuration data as arguments for the service calls. Eg.
[kbmMW_Method('EchoReversedConfigString')] [kbmMW_Rest('method:get, path: "echoreversedconfigstring"')] // This method obtains its value from the configuration. function ReverseStringFromConfig([kbmMW_Config('a.b.c.value')] const AString:string):string;
Upon calling the echoreversedconfigstring method it’s AString argument will always contain a string value read from the configuration.
This concludes this blog about the new configuration features in the upcoming kbmMW release.