Tamaya Model Documentation and Validation (Extension Module)

Overview

The Tamaya model module provides support for documenting configuration and validating configuration read and processed against this model. Documentation and config models can be provided in different ways:

  • as separate meta-model documents

  • by providers that check classes/packages for configuration annotations (planned)

Compatibility

The module is based on Java 7, so it will not run on Java 7 and beyond.

Installation

To benefit from configuration builder support you only must add the corresponding dependency to your module:

<dependency>
  <groupId>org.apache.tamaya.ext</groupId>
  <artifactId>tamaya-model</artifactId>
  <version>{tamayaVersion}</version>
</dependency>

Describing the Configuration Meta-Model

Basically configuration is modelled using key, valuje-pairs of Strings. Looking at a keys a.b.c.param1 and a.b.c.param2 the following concepts can be used to defined/describe configuration:

  1. the configuration section: In our case this equals to a.b.c, which itself also includes the tranitive entries a.b and a.

  2. the configuration parameter: Basically parameters are adressed using their fully qualified names, which equals to the containing section name and the relative parameter name, separated by the dor separator. In the above example a.b.c.param1 and a.b.c.param2 are the fully qualified parameter names.

Now with only these 2 concepts a simple configuration meta-model can be defined as

  • a meta-model’s name, used just for grouping different meta-models and entries to better separate descriptions, e.g. in a management console or generated configuration documentation site.

  • a set of sections.

  • a set of parameters.

  • Both, sections as well as parameters,

    • are prefixed with the meta-model selector, by default {model}..

    • are identified by their fully qualified names, where '.' is used as a separator character.

    • may be required, or optional

    • may have an optional description

    • may have additional custom configModels configured.

  • Parameters additionally have

    • a type, described by the fully qualified class name, into which any configured (String) value must be convertable into. If no type is configured java.lang.String should be assumed as default.

    • an optional regular expression that can be used to validate the String values returned from a configuration.

Given these concepts a configuration can be fully described. Entries that are not contained in one of the given sections (or its children), or parameters not described or marked as valid (e.g. for dynamic configModels of a section), are called undefined. Undefined parameters should be grouped with its parent section. Each section, as well as all parent sections, including transitive) of any parametet read, should similarly marked as undefined, if and only if

  1. the section itself is (directly) undefined

  2. the section is not a super section of a defined section.

As en example the section definition of a.b.c also implicitly includes the sections a.b and a to be defined sections, despite the fact that section properties, such as description and custom configModels are not inherited to its parent, or child section.

Defining Meta-Configuration Model

The configuration meta-model is defined by simple configuration entries. The root section for all model configuration by default is called {model}. Within this section fully qualified configuration keys defines which part of the configuration is targeted by certain entries.

Defining Sections

First we start to define some configuration sections, the example below starts with the most important variants supported:

# Metamodel information
{model}.provider=ConfigModel Extension

# org.mycompany.root (optional section)
{model}.org.mycompany.root.class=Section
{model}.org.mycompany.root.description=Root section defining my company configuration.

# org.mycompany.security (required section)
{model}.org.mycompany.security.class=Section
{model}.org.mycompany.security.required=true
{model}.org.mycompany.security.description=Security related settings.\
         refer for further details to XXX.

# minmal section
{model}.minimal.class=Section

# custom validated section
{model}.validated.class=Section
{model}.validated.configModels=org.apache.tamaya.model.TestValidator

Above org.mycompany.root transitively defines 3 sections:

  • org

  • org.mycompany

  • org.mycompany.root

All sections are optional. Additionally the model above also defines a required section org.mycompany.security. Required sections are checked so the section is not empty. It is not checked for any specific parameter hereby, only the existance of any child parameter is validated.

The class attribute has to be defined for any section definition, because if not set a model entry is, by default, defined to be a parameter configModel entry. Given above the entry for the section minimal shows such a minimal entry.

validated defines a section, which is validated through a customizable validator. Hereby an ordered list of validators can be provided, separated by commas.

Defining Parameters

Similarly parameters also can be defined:

# org.mycompany.root.name (required parameter)
{model}.org.mycompany.root.name.class=Parameter
{model}.org.mycompany.root.name.required=true
{model}.org.mycompany.root.name.description=The company's name, also used in the application's main header.

# org.mycompany.security (required parameters)
{model}.org.mycompany.security.uid.required=true
{model}.org.mycompany.security.uid.description=The user id.
{model}.org.mycompany.security.realm.required=true
{model}.org.mycompany.security.realm.configModels=org.apache.tamaya.model.RealmValidator
{model}.org.mycompany.security.real.description=The security realm required.
{model}.org.mycompany.security.tokenid.description=The token id, if the token service is used (optional).

# A minmal parameter
{model}.minimalClass.class.type=Class

Similarly as when defining section also parameter entries define transitively its containing sections. E.g. the entry above for org.mycompany.security.realm also defines the following sections (as optional).

  • org

  • org.mycompany

  • org.mycompany.security

Additional entries for section, e.g. configModels to be done, can be added as described in the previous section, but are optional.

Since the parameter is the default type for model entries, a minmal parameter model entry only only needs it’s parameter type to be defined. In the example above we define a parameter minimalClass of type Class. Types hereby are fully qualified class names, whereas as 'java.lang' for built-in language types can be ommitted.

Model Locations

By default the configuration model can be defined at the following locations:

  • classpath*:META-INF/configmodel.properties, separate to the current Configuration. This functionality is enabled by default, but can be disabled by adding org.apache.tamaya.model.default.enabled=false to your current Configuration.

  • implicitly as part of the current +Configuration. THis can be disabled by setting the org.apache.tamaya.model.integrated.enabled configuration poarameter to false.

  • customized by configuring the org.apache.tamaya.model.resources in the current Configuration. This parameter allows to define the locations from where the model extension is trying to read the model configuration. If the resources extension is available in your system it is used to evaluate the locations. If not the default Classloader.getResources command is issued. Also it is required that the formats extension is available, since this is used to effectively read the data. This extension also allows you to use alternate representation formats such as ini, xml, yml, json.

Programmatic API

Basically the configModel module provides a simple API to access the defined ConfigModel instances and validating the current Configuration against the models as follows:

public final class ConfigModelManager {

    private ConfigModelManager() {}

    public static Collection<ConfigModel> getModels();
    public static Collection<ConfigModel> findModels(ModelType type, String namePattern);
    public static <T extends ConfigModel> T getModel(String name, Class<T> modelType);
    public static Collection<ConfigModel> findModels(String namePattern);

    public static Collection<ValidationResult> validate();
    public static Collection<ValidationResult> validate(boolean showUndefined);
    public static Collection<ValidationResult> validate(Configuration config);
    public static Collection<ValidationResult> validate(Configuration config, boolean showUndefined);

    public static void registerMBean();
    public static void registerMBean(String context);

}

This singleton allows to validate the current or any Configuration instance. All the ConfigModels read also are available from the getModels method. This models can be used to provide documentation, e.g. as part of a CLI interface or shown on a documentation web server.

A ConfigModel hereby is defined as one single part of configuration, typically corresponding to a specific concern of your system. As an example you can define different models for different modules or products plugged together. With resolution mechanism in place you can also define a shared module that is targeted by multiple modules as a single configuration source (e.g. for configuring the machine’s IP address and subnet settings only once.

public interface ConfigModel {

    ModelType getType();
    String getName();
    String getProvider();
    boolean isRequired();
    String getDescription();
    Collection<ValidationResult> validate(Configuration config);
}

Hereby ModelType defines more details on the kind of model:

public enum ModelType {
    /**
     * A configuration section.
     */
    Section,
    /**
     * A configuration paramter.
     */
    Parameter,
    /**
     * ConfigModel to ensure a certain configuration filter is installed.
     */
    Filter,
    /**
     * ConfigModel to ensure a certain combination policy is active.
     */
    CombinationPolicy,
    /**
     * ConfigModel that is a container of other validations.
     */
    Group,
    /**
     * ConfigModel to simply check availability for a class on the current classpath.
     */
    LoadableClass
}

A ValidationResult models one validation executed by a ConfigModel on a certain Configuration instance:

public final class ValidationResult {

    public static ValidationResult ofValid(ConfigModel configModel);
    public static ValidationResult ofMissing(ConfigModel configModel);
    public static ValidationResult ofMissing(ConfigModel configModel, String message);
    public static ValidationResult ofError(ConfigModel configModel, String error);
    public static ValidationResult ofWarning(ConfigModel configModel, String warning);
    public static ValidationResult ofDeprecated(ConfigModel configModel, String alternateUsage);
    public static ValidationResult ofDeprecated(ConfigModel configModel);
    public static ValidationResult ofUndefined(final String key);
    public static ValidationResult of(ConfigModel configModel, ValidationState result, String message);

    public ConfigModel getConfigModel();
    public ValidationState getResult();
    public String getMessage(),
}

The result of a complete validation on a concrete Configuration instance finally is mapped as a Collection<ValidationResult>, refer to the methods on ConfigModelManager.

Auto-Documentation of Classes with Configuration Injection

A special feature of this module is that it observes ConfigEvent published through Tamaya’as event channel (tamaya-events module). If no metaconfiguration model is found the model manager by default automatically creates models for all injected instances on the fly. In the case of CDI integration this happens typically during deployment time, since CDI initializes during deployment time. Other runtime platforms, such as OSGI, may have rather different behaviour. Nevertheless this means that after your system has been started you should have access to a complete set of ConfigModel instances that automatically document all the classes in your system that consume configuration (through injection).

Model SPI

Registering Configuration Models

The model extension also provides an SPI where customized functionality can be added. The main abstraction hereby is the ModelProviderSpi interface, which allows any kind of additional config models to be added to the system:

public interface ModelProviderSpi {

    Collection<ConfigModel> getConfigModels();

}

New instances implementing this interface must be registered into the current ServiceContext, by default the ServiceLoader is used.

Other Utility Classes

The module also provides further utility classes that may be useful for implementing models or testing:

  • AbstractModel provides a base class that can be extended, when implementing ConfigModel.

  • AreaConfigModel provides a ConfigModel implementation (with a corresponding Builder) to model the requirement of certain configuration sections being present, or opionally present, in the model.

  • ParameterModel provides an implementation base class for validating parameters on existence and compliance with a regular expression.

  • ConfigDocumentationMBean is the MBean registered that models similar functionality as ConfigModelManager.

  • ConfigModelGroup provides a ConfigModel that groups several child models.

  • ConfigModelReader allows to read ConfigModels from properties files as described at the beginning of this document.

Switches to enable/disable functionality

The model module provides different switches that can be used to activate or deactivate features:

  • org.apache.tamaya.model.integrated.enabled allows to deactivate reading inline metaconfiguration delivered with the normal Tamaya Configuration. By default inline entries ({meta}...) are evaluated.

  • org.apache.tamaya.model.default.enabled allows to deactivate reading metamodel information from classpath:META-INF/configmodel.properties. By default it is active.

  • org.apache.tamaya.model.resources allows to defie additional resources (loaded through the resources extension), that can be used to read metamodel information in any format using Tamaya’s format module.

  • the system property org.apache.tamaya.model.autoModelEvents allows to activate/deactivate the automatic documentation of classes configured and published by Tamaya ConfiguredType event instances (e.g. published by Tamaya’s injection modules).