-
Notifications
You must be signed in to change notification settings - Fork 7
Configuration Framework
The universAAL configuration Framework has two features that no other configuration framework possesses:
- Distribution: The ability of sharing and managing configuration In a distributed environment i.e: exchanging configuration information between nodes in the same network.
- Semantic: The units of configuration can be described, in fact is mandatory for all to provide a human readable description, and in the case of parameters their type can be specified.
The basic approach for configuration in universAAL is that each of the instances in the uSpace will contain a special module, the Configuration Manager which, as its name implies it will manage the configuration of each instance, and collaboratively the configuration of the whole uSpace.
Modules that require configuration register the configuration they need in the Configuration Manager of their Instance. Upon registry, the Configuration Manager will serve back the latest configuration, which is persistently stored anytime a configuration change is requested. Both configuration requests, and initial configuration is done using a callback notification type mechanism. If there is no information about the latest configuration state, the configuration manager will try to provide the default configuration (if the registering module provides it) and also store it as current configuration state.
Configuration Modules will distribute the configuration, enabling on one hand that certain types of configuration to be redundantly stored across all Configuration Managers in the uSpace; and on the other Configuration Managers to change configuration remotely of other Configuration Managers. Configuration Managers offer an interface for Configuration Interfaces for human users to check and change configuration. Every time a user changes a configuration entity it is distributed, stored and the affected modules are notified with the change.
If you are only interested in developing applications in universAAL you should only read this section. We will provide the information needed to effectively use the configuration framework of universAAL.
Before going in to implementation details, different definitions used to provide the characteristics of the configuration required by your module will have to be explained.
The unit of Configuration information is defined as a Described Entity (DE); it has a unique identifier (see later, Scope) and an internationalizable description. DEs are used to define the configuration required by modules, and the Configuration Manager is based upon this interface to identify store and update the configuration.
There are two main classes of configuration units. The first one is a Configuration Parameter, this interface is used to describe a single parameter to be configured. By fixing type (getType method) developers can define explicitly which type is the configuration. The available types are the ones defined by the datarepresentation universAAL module, therefore all primate types can be defined (eg, Boolean, Integer, Double, String,…), yet more complex types can be defined. For example a parameter can be defined as an enumeration (one value out of a discrete set), as a list (when cardinality is greater than 1), as a combination of other types (using intersections and unions), as ranges (eg: an Integer between 0 and 100), etc . The module must provide a type, null types will fail to register. The types will be checked, so each value that is attempted to be set (by the default value, or by configuration interfaces) will be checked before is sent, therefore the default value provided must be a member of the defined type. If a default value is set to null, it will be interpreted as the parameter being optional, thus no default configuration will be provided upon registration.
The other main definition type is the Configuration File, this interface is used to define the entity as a whole file. Note that certain files, like java properties file (or xml equivalent) make much more sense to be divided into a set of different configuration parameter entities rather than a single file, the Configuration Parameter interface offers more control over the number and type of each parameter. Configuration File entity should be used when the configuration is a file in itself, for example a binary file containing a certificate, a complex or standard xml configuration or a zip file. Configuration File can also provide a default file, in this case is provided through an URL where the file will be found, this allows providing the file as a resource in the executable file of the module, or through the network (even maybe through the resource manager). Another useful parameter, especially for configuration interfaces, is the file extension expected to have the compatible files. It is provided through the getExtensionFilter method and it should be a comma separated filter expression( e.g: “*.jar,*.zip”).
The last definition type is not a configuration definition itself, it is used to express the need of connecting an existing definition to a Configurable Module, This way when a configuration is changed all modules registered to that configuration entity will be notified, effectively sharing the same configuration.
To uniquely identify each entity the Scope class is provided. Every Scope contains an id, but the scope can be determined according to the expected usage of the configuration entity:
- SpaceScope: modules defining or consuming DEs from this scope are all over the uSpace. The configuration described by this scope may apply to the uSpace itself, in this case take care not confuse configuration with possible profiling definitions of the uSpace itself. uSpace scope can be seen as a global placeholder for configurations that apply all over the uSpace.
- ApplicationScope: When configurations apply only for an application this scope should be used. The scope still applies to the whole uSpace (it is distributed along all the instances). Application id is provided per application, this way two different applications can use the same scope id without interfering with each other, as long as their appId is different.
- InstanceScope: This scope is restricted to the instance itself. Therefore it will not be distributed (although it still can be queried and changed remotely). Modules inside the same instance can share the instance scope configurations. These configurations may configure the instance itself.
- ApplicationPartScope: The same as ApplicationScope, but one level of granularity finer, i.e: a part within an application can define its own scope, so it is not interfered by other parts’ configuration.
- ModuleScope: same as instance scope, but on a module level. This is the framework equivalent to OSGi configuration service, in the sense of a single module having a locally managed configuration space.
Once the definitions are clear the implementation is straight forward, and it can be basically resumed in 3 steps:
- Create a Configurable Module. A module that implements the Configurable Module, so it contains the callback method for the Configuration Manager to provide the configuration.
- HINT: the configurable module can be the module activator class, so the configuration of the module is centralized.
- Locate the Configuration Manager. A snipped can be found in the sample provided, but basically is using the universAAL container interfaces to locate the service.
ConfigurationManager configM = (ConfigurationManager) context.getContainer().fetchSharedObject(context, new Object[]{ConfigurationManager.class.getName()});
- Register the configuration Modules. Upon registration configurable modules will be associated to the entities they respond to.
configM.register(ConfigurationCentral.configurations, MyConfigurableModule);- HINT: more than one configurable module can be registered, so the configuration can be directly provided to the classes that need it.
- HINT: the list (or array) of described entities can be implemented as a static property (or method) of the Configurable Module, so the configuration is defined and processed in the same source file.
Configurable Module interface defines a single method to be extended, configurationChanged with 2 parameters: a Scope and a value. This method will be called every time an entity changes (and during initialization, upon registration), the Scope of the entity will be provided, and the new value.
Configuration Modules should only change the internal values represented by the DEs through the interface method. This will maintain consistency of the configuration as a whole. If the confiruation has to be changed within the Configurable Module please read the Advanced Configuration Definition section.
Notice the type of value is an Object; the runtime type will depend on the definition provided by the DE. So if it is defined as an Integer it will receive an Integer java class, if cardinality is greater than 1, then the expected type is a List; if the DE is a Configuration File the expected type will be a java File class (if the file could not be cached by the framework a java URL class may be provided).
The value is always checked for compliance with the Type Expression provided for the Configuration Parameter, expect always the correct type.
The configurationChanged returns a boolean, which indicates the success of the changed configuration. For example if the value is intended for a device, and even though the value is within the accepted range if de device rejects it then the method should return false. Same holds true for files that could not correctly be interpreted. The return value will be used to notify the configuration interface about the unsuccessful operation. It is recommended to log the concrete error as to why the new value was not accepted.
There is a very simple command line configuration editor provided with universAAL (only under karaf distributions). To install it run the command:
bundle:install -s mvn:org.universAAL.middleware/mw.karaf.shell.universAAL.config.osgi
available commands are:
-
universAAL:configList [regExp] [locale]
- Discover the existing Configurable Entities
- regExp will be used to filter ids in the scopes of the configuration entitites.
- locale by default it will use en, but it could be changed (two letter) to use for descriptions.
-
universAAL:configEdit parameter [value]
- Change the value of a given Parameter
- parameter Parameter id to change.
- value new Value of the parameter to be changed, if not set default value is set.
-
universAAL:configPull parameter path
- Get current Configuration file of a configuration file
- parameter Parameter referring to the file to pull.
- path Local Path to copy the file to.
-
universAAL:configPush parameter path
- Set the a Configuration file for a configuration file entity.
- parameter Parameter referring to the file to pull.
- path Local Path where the file to set resides.
Not all applications should provide a configuration interface; a configuration interface is analogous to an UI handler in the UI bus. It is used to present the configuration to a user, and enable the user to change the configuration of each DE.
The Edition of configuration is an extension of the definition. All the functionality of the DE is extended so it can be edited.
For both configuration parameter and file there is a default set, so the root entity configuration has the methods to directly set to default and check if the default is already set. DE does not offer the current value as main functionlity, so for each DE a getter is provided (getConfiguredValue, in case of Configuration Parameter type and pullFile in case of Configuration File type). For edition a setter is also provided (setValue for parameters, pushFile for files).
In case of Pull and Push file the actual file content will be transmitted if necessary. It is recommended to use temporal files, in fact pull file returns always a temporal file (never the actual file) that should be deleted once it is closed (or copied to the final destination).
The interfaces hide the implementation, which will be provided per each requested DE depending on whether if it can be managed locally or it is a remote configuration (an instance or module scope on another instance).
To request the Configurable Entity Editors (CEEs, the objects that actually change the edition of each DE), a filtering system has been added. The filter consists of a list of patterns that is “anded”, i.e: all patterns must match in other to consider a DE requested. All these patterns are described in Table:
| Pattern | Description |
|---|---|
| ConfigurationFileTypePattern | Matches only ConfigurationFile defined Entitites. |
| ConfigurationParameterTypePattern | Matches only ConfigurationParameter defined Entities. |
| IdPattern | Match Entities with a specific Id. |
| ApplicationPattern | Matches entities with an application type scope. |
| ApplicationPartPattern | Matches entities with an application part type scope. |
| InstancePattern | Matches entities with an Instance type scope. |
| ModulePattern | Matches entities with an Module type scope. |
| NotPattern | Negate any given pattern. |
To enable dynamic interfaces to be implemented there are some aspects to take into account.
The first, and most important is the fact that configuration is queried (description, type, current value) and modified through the CEE objects. Configuration interfaces will receive an implementation of each DE requested; this is so MVC paradigm can be applied on an entity basis.
Another aspect is that when requesting CEEs, the native way is by registering a listener to the Configurator Editor (which is implemented by the same class that implements Configuration Manager). This is an asynchronous way of requesting CEEs, configuration interfaces should be able to dynamically add CEEs to the view. This enables rendering of local CEEs quickly, while remote CEEs are being transmitted through the network. The editor in this “monitor mode” will accept new configurations (that match the given pattern) and are added to the view asynchronously. There is a synchronous mechanism too, but the implementation is a 10 second wait, which is not always practical.
Each CEE is capable of issuing events, these indicate when a DE has changed value, it is recommendable that the views per CEE also subscribe to the changes by using the subscribe2changes method. This way editor interface in “monitor mode” can also update the displayed values of the CEEs.
CEEs are managed by the Configurator Editor, and do not need to be disposed of in any special way, when finished just use the traditional “java no reference count” so the garbage collector removes it. In the same manner Configurable Entity Managers don’t need to be unregistered, although it is always a good idea, references maintained by Configurator Editor are weak references and are protected.
Configuration will not only be provided by configurator interfaces or the storage, sometimes modules can self-adjust configuration. The advanced feature of the configuration enables precisely this, modules being able to tune their configuration from within while maintaining the rest of the configuration framework(storage and configuration interfaces) consistent with the change.
As a side effect of this configuration framework could be used as a realtime telemetry system.
To define a DE is capable of self-configuration in addition to the interface from Figure 2 the DE must implement another interface: DynamicDescribedEntity (DDE). This interface will just enable a component to subscribe to the events.
When de DDE is registered, the Configuration Manager will register a DynamicDescribedEntityListener object which should be notified when either the description or the value (or file content) changes. There are two separate methods in this interface for each change.
The implementation is quite similar to any DE, make sure it is registered upon registration a Dynamic Described Entity Listener will be registered. When the internal configuration changes just call the Listener, it will take care of validating, storing, propagating (announcing on the network the change) and notifying the required Configurable Modules.
Do not notify the listener within the configurationChanged method stack or it will cause an infinite loop (if done it must be done with this run condition in mind).
Found a problem?
- Report suggestions, missing, outdated or wrong documentation creating an Issue with "documentation" tag
Support:
Found a problem?- Report suggestions, missing, outdated or wrong documentation creating an Issue with "documentation" tag