ALox  V. 2402 R. 0
Home ALox for C++ ALox for C# ALox for Java Download
Classes
cs.aworx.lib.config Namespace Reference

Namespace Description


Note
It may be helpful to read this namespace documentation and understand the concepts - even if you are not planning to use configuration variables in your own code. The reason is that with the knowledge of the principals described here, it is possible to influence the behavior of 3rd party code (code of other team members or ALib enabled libraries like for example ALox Logging Library).

1. Introduction

This package provides tools to read and write configuration data using different mechanisms. In short, the features are:

Class Configuration and Plug-ins:

The plug-ins, derived from abstract class ConfigurationPlugin are plugged into an object of type Configuration and are responsible for reading (and optionally writing) variables from dedicated sources.

Class Configuration offers an interface to retrieve and store configuration variables using the plug-ins. The plug-ins are attached to the Configuration instance along with a unique priority. Values are retrieved and stored by looping through the plug-ins sorted by priority. As soon as one plug-in confirms to have found (or stored) the variable, the loop ends. This way, plug-ins may "overrule" each other in respect to retrieving configuration data.

Class ALIB creates a static singleton of this class for public access and this singleton is usually all that is needed.

Naming Configuration Variables:

Configuration variables are addressed by two strings: The category and the name. In the case of INI files, the category is translated to the section and the name to variables within the section. Other plug-ins, for example CLIArgs or Environment, are prepending the category name to the variable name separated by an underscore character '_'. For example, a variable in category "ALIB" named "WAIT_FOR_KEY_PRESS", in an INI file would be stated like this:

    [ALIB]
    WAIT_FOR_KEY_PRESS= yes

To define the same variable and value on the command-line, the parameter needs to be:

    --ALIB_WAIT_FOR_KEY_PRESS=yes

The anonymous, empty category is allowed. For convenience, with variables of this category, no underscore is prepended by the plug-ins. The little drawback of this is, that variables in the anonymous category that contain an underscore character, for some plug-ins can get ambiguous. For example, variable MY_VARIABLE in the anonymous category, on the command line would be uniquely addressed by:

    --MY_VAR=my value

In an INI file, two ways of specifying the variable are possible (and therefore ambiguous). In the anonymous section:

    MY_VAR      =       my value

or, in section MY:

    [MY]
    VAR         =       my value

2. Command-Line parameters, Environment Variables and INI files

Three different standard plug-ins that collect external configuration variables are provided with ALib already:

The proposed priorities to be used when attaching the plug-ins to the Configuration object, are given in static fields:

With these default priorities set, whatever is stored in an INI file, can be overwritten by setting an environment variable. Both settings can in turn be overwritten by specifying a corresponding command line parameter when launching the process!

3. Default Variables

In addition to the three plug-ins described above, ALib implements a fourth one, class InMemoryPlugin. As the name indicates, this class keeps configuration variables in memory. The special thing about it is that it does not read any external data source! The reason for having it, is twofold. The first use case are Default variables.

An instance is plugged into class Configuration with a priority of

this plug-in serves as a storage for default values. The only way to set these default values is programatically, which means "with program code".

The advantages of having such default variables are:

4. Protecting Variables

Class InMemoryPlugin is by default used a second time by plugging an instance into class Configuration with a priority of

When setting a variable within this plug-in, no other plug-in can 'overrule' this value. This way, it is possible to protect values against external modification.

Note
One might think: "If I do not want to allow external modification, I rather hard-code everything". This of-course is true for code under control. However, for 3rd party code using ALib, this offers an easy way to disallow users of your software (which incorporates that 3rd party code) to configure things that you do not want to be configurable.

5. Using class Configuration

In normal use cases, there is no need to create an instance of class Configuration, as a default singleton is provided with ALIB.Config. The command line parameters (optionally) provided with ALIB.Init are passed to the command-line plug-in of this singleton.

Class Configuration in the constructor by default sets up four plug-ins automatically: CLIArgs for parsing command-line arguments as variables, Environment for environment variables and two plug-ins of type InMemoryPlugin used for default and protected values.

If an application wants to suppress the use of one of the plug-ins, the plug-ins can be removed using method RemovePlugin.
On the other hand, a plug-in of type IniFile may be attached on bootstrap of a process using Configuration.PrioStandard (20000).

In addition (or alternatively), custom plug-ins may be written and installed using arbitrary priorities.

Class IniFile is designed for simplicity and smaller applications. Instead of using it IniFile, it is recommended to use application/platform specific mechanisms for writing configuration data. In this case, write your own plug-in to grant ALib and other libraries which rely on ALib, access to your applications's configuration data. Again, ALib here follows its design principle to be non intrusive: The plug-in concept allows users of ALib (more important: users of ALib enabled libraries) to expose any external configuration source to these libraries. This way, the users can stick to his/her preferred way of implementation.

6. Variable Substitution

Method Configuration.Load by default substitutes references to other configuration variables found in the value of the requested variable.

For example, if two variables are defined as follows:

    MYCAT_RESULT= 42
    MYCAT_MYVARIABLE= The result is $MYCAT_RESULT

then, with the retrieval of variable MYCAT_MYVARIABLE, variable MYCAT_RESULT is read and the substring "$MYCAT_RESULT" is substituted by "42".

Substitutions are performed repeatedly until all variables are resolved. Therefore, nested substitutions may be defined as well. To avoid an endless loop in case of circular dependencies, a simple threshold applies: A maximum of 50 replacements are done.

The dollar sign '$' used to recognize variables is the default and can be modified. It is also possible to specify a prefix and a suffix for the identification of substitutable variables in other variables' values. For example, the syntax can be adjusted to

    MYCAT_MYVARIABLE= The result is %{MYCAT_RESULT}

See documentation of fields SubstitutionVariableStart, SubstitutionVariableEnd and SubstitutionVariableDelimiters for more information.

When parsing a variables' category and name, method Configuration.Load searches for an underscore character '_'. The first underscore found serves as a delimiter of category from the name. If no underscore character is found, the category is left empty (anonymous category) and the name is set to what is given as a variable name.

Note
This implies that variables which have no category but have an underscore in their name, need to be specified with a leading underscore. For example:
    TEXT= Welcome to $_HOME_LOCATION
refers to a variable name HOME_LOCATION in the anonymous category "".
For clarification:
  • This is not needed (but also no fault) for variables that do not contain an underscore in their name
  • This must not be done if a variable belongs to a named category.

7. Loading and Storing Variables

Values of variables are loaded and received using instances of class Variable. Instances of this class can be passed to the interface of class Configuration or directly to specific ConfigurationPlugin objects.

Simple access methods allow to read or set the values of a variable.

By consequently using optional class VariableDecl for declaring all variables of an application or library, all attributes of variables can be maintained in one place. This includes categories, names, comments, value delimiters and default values of variables. Class Variable accepts instances of VariableDecl in the constructor as well as in overloaded method Variable.Declare.

8. In- and Externalizing Variable values

Variable values provided via command line parameters, environment variables or textual configuration files need to be converted when loading and storing them. This has two reasons:

For this task, class XTernalizer is used. Each ConfigurationPlugin owns an instance of this class (which is exchangeable). Conversion is done internally and there is no need to interface with this class directly when using variables. A huge benefit of this API design is that variable values look the same in an INI-file as when passed as a command line parameter.

But it is important to understand that value parameters provided with interface methods to store variables accept "externalized" strings only. In simple cases this is not relevant. But if a variable contains multiple values or special characters, it has to be understood that 'internalization' of the value takes place.

Normal, internal string values need to be added to the variable in code prior to invoking a store method. If an externalized string is provided with the store methods, then previously added variable values are cleared!

Classes

class  CLIArgs
 
class  Configuration
 
class  ConfigurationPlugin
 
class  Environment
 
class  IniFile
 
class  InMemoryPlugin
 
class  Variable
 
struct  VariableDecl
 
class  XTernalizer