Apache Commons logo Commons Configuration

Creating Configurations

Before a configuration and the data it contains can be accessed it has to be created and initialized first. Although the concrete Configuration implementations provided by Commons Configuration typically have a public default constructor, instances should only be created directly in exceptional cases. The recommended way is to use a configuration builder.

Configuration Builders

Commons Configuration defines the generic ConfigurationBuilder interface which is used for creating initialized configuration objects. The interface defines a getConfiguration() method returning a generic type derived from Configuration. Here a specific Configuration implementation class can be inserted.

Note that the name of this method is getConfiguration() and not createConfiguration(). This is because a builder is not required to create a new instance on each invocation. Rather, a builder is also responsible for managing the instance it has created. A basic implementation may create its managed configuration once on first access and will then always return the same instance in its getConfiguration() method. A builder which is aware of reloading in contrast may invalidate its managed configuration when it detects that the content of the configuration has changed on disk; then the next invocation of getConfiguration() returns an updated configuration object. (Reloading is discussed in more detail in a later section of this user's guide.) Builder implementations must be thread-safe so that it is guaranteed that they behave correctly even if accessed concurrently by multiple threads.

The recommended usage for accessing configuration data from multiple parts of an application is to create a builder object initially and keep a reference to it centrally. Whenever configuration information is needed the builder is asked for its Configuration object; from there the actual configuration settings can be obtained. While this approach introduces another level of indirection, it enables the application to add new functionality transparently by replacing the builder implementation. For instance, if at a later stage the requirement occurs to react on external changes of configuration data, the builder object can be replaced by a reloading-aware builder. This enables support for reloading all over in the application immediately without having to change anything.

BasicConfigurationBuilder

Commons Configuration provides multiple concrete ConfigurationBuilder implementations supporting different features. The most basic implementation is BasicConfigurationBuilder. It is the base class for all other builder implementations and defines a framework for creating and initializing configuration objects. The functionality provided is as follows:

  • When an instance of BasicConfigurationBuilder is constructed the class of the Configuration implementation to be created has to be passed. This class must be compatible with the generic type parameter of the builder.
  • It is possible to set arbitrary initialization parameters for the configuration object to be created. Such parameters correspond to the special properties offered by the configuration class (e.g. the throwExceptionOnMissing flag, the object for handling lists, helper objects for variable interpolation, and so on).
  • The getConfiguration() method checks whether the managed configuration instance has already been created. If so, it can be directly returned.
  • If this is the first access to getConfiguration(), the managed configuration object is created. This is done via reflection: the configuration object is created, and all initialization parameters which have been set are applied.
  • There is also a reset() method which removes the managed configuration instance. Calling this method causes a new instance to be created the next time getConfiguration() is invoked.
Note that these methods are all properly synchronized so that the builder class is thread-safe.

The following code fragment shows how a BasicConfigurationBuilder can be used to create an empty PropertiesConfiguration object. At this stage of the discussion, the details of this example will not yet be understandable; they will be explained in the following sections. This is just to get a feeling how the usage of configuration builders looks like in practice:

Parameters params = new Parameters();
BasicConfigurationBuilder<PropertiesConfiguration> builder =
        new BasicConfigurationBuilder<PropertiesConfiguration>(
                PropertiesConfiguration.class)
                .configure(params.basic()
                        .setListDelimiterHandler(
                            new DefaultListDelimiterHandler(','))
                        .setThrowExceptionOnMissing(true));
PropertiesConfiguration config = builder.getConfiguration();

Initialization Parameters

Depending on the concrete Configuration class to be instantiated, it can be necessary to set a bunch of initialization parameters. In order to simplify this and make the code somewhat concise, a fluent API is provided for setting initialization parameters. Basically, initialization parameters are defined by POJOs (plain old Java objects) with properties corresponding to the special properties supported by the configuration object to be created. In the package org.apache.commons.configuration2.builder.fluent a number of interfaces is contained defining the possible initialization parameters for the standard Configuration implementations shipped with Commons Configuration. These interfaces form a natural inheritance hierarchy corresponding to the inheritance graph used by concrete Configuration implementations. So there is a fundamental set of initialization parameters supported by all classes derived from AbstractConfiguration. Configurations loaded from a file also support these parameters plus additional ones for defining the file to be loaded. An XML-based configuration supports all basic and file-related parameters plus specific parameters defining its specific properties, and so on.

Exposing such parameter objects via a fluent interface becomes tricky in Java if inheritance is involved. In Commons Configuration the Parameters class is responsible for the creation of parameters objects. It serves as a type-safe factory for parameter objects with support for inheritance. It defines a set of methods for creating parameter objects for special Configuration classes. On the objects returned by these methods fluent set methods can be invoked in order to set the single properties. As an example consider the following code fragment which defines some properties for an XMLConfiguration:

Parameters params = new Parameters();
XMLBuilderParams xmlParams = params.xml()
    .setThrowExceptionOnMissing(true)
    .setValidating(true)
    .setEncoding("UTF-8")
    .setFileName("config.xml")
    .setExpressionEngine(new XPathExpressionEngine());

Note how properties from different parameter interfaces can be set in an arbitrary order: the throwExceptionOnMissing flag is part of the basic initialization parameters common to all configuration classes, the encoding and the file name parameters are common to all file-based configurations, the expression engine parameter is supported by all hierarchical configurations, and the validating flag is specific to XML configurations. We will not describe all available initialization parameters in detail now; they are explained in the sections dealing with specific Configuration classes (and of course, the Javadoc is the ultimate reference). For now a short overview over the existing parameter objects and the corresponding methods in the Parameters class should be sufficient:

Parameters method Interface Description
basic() BasicBuilderParameters Defines fundamental properties common to all Configuration implementations derived from AbstractConfiguration.
fileBased() FileBasedBuilderParameters Properties related to file-based configurations. For instance, multiple ways for defining the file to be loaded are provided.
combined() CombinedBuilderParameters This object is used by the specialized builder for combined configurations. Here properties can be set which define the content of the resulting combined configuration.
jndi() JndiBuilderParameters A parameters object for initializing JNDI configurations.
hierarchical() HierarchicalBuilderParameters Here special parameters common to all hierarchical configurations are defined, for instance the expression engine.
xml() XMLBuilderParameters The parameters for XML configurations.
properties() PropertiesBuilderParameters The parameters for properties configurations.
multiFile() MultiFileBuilderParameters This parameters class is used by the builder for multi file configurations.
database() DatabaseBuilderParameters The parameters for DatabaseConfiguration.

After a parameters object has been created and initialized via its fluent set() methods, it can be passed to a configuration builder's configure() method. This method extracts all properties from the passed in object and stores them internally. They are then used to initialize a newly created Configuration object. Calling configure() another time with a different parameters object overrides all properties set so far; more precise, the existing properties are cleared, and the new ones are copied over. However, it is possible to pass multiple parameters objects at once to the configure() method (it has a varargs argument). In this case, the union of all parameters is constructed.

Configuring a configuration builder with parameters objects is an expressive and type-safe way. For initialization parameters constructed more dynamically there is an alternative based on maps. Some constructors of BasicConfigurationBuilder accept a Map<String, Object>. Here arbitrary initialization parameters can be passed. The keys of the map are strings corresponding to the names of the initialization parameters (they are equivalent to the property names in the associated Configuration implementations; for instance throwExceptionOnMissing); the map's values are the values of the parameters. No matter which mechanism is used to define initialization parameters, it has to be ensured that the configuration object to be constructed supports all of these parameters; otherwise, an exception is thrown when the instance is created.

Default Initialization Parameters

Big applications may use configuration data from multiple files or sources. If they need special settings for all their configuration objects, there is the issue that these settings have to be repeated again and again for each configuration source to be created. For instance, all files to be read may have a specific encoding, or hierarchical configurations should use a special expression engine. In a naive approach, all these settings have to be set on each configuration builder used by the application.

To make life of developers easier and in compliance with the DRY (don't repeat yourself) principle, Commons Configuration supports default initialization parameters for configuration sources. It was already shown how an instance of the Parameters class is used to create initialization parameter objects for various types of configuration sources. In addition to the methods for creating these objects, Parameters also deals with default values for them. The mechanisms are as follows:

Parameters defines methods for registering so-called defaults handler objects. A defaults handler is an object implementing the DefaultParametersHandler interface. This interface defines a single method which accepts a parameters object of a generic type and populates it according to its logic. Such handlers can be registered for specific initialization parameter interfaces.

When an initialization parameters object of a specific class is to be created the Parameters instance checks whether DefaultParameterHandler objects have been registered for this class or its base classes. If this is the case, the matching handler objects are invoked on the newly created parameters object - they can now initialize it as they like.

Note that the inheritance hierarchy of parameters objects is implicitly taken into account: A defaults handler registered for file-based parameters is also invoked for XML parameters because XML parameters are derived from file-based parameters and thus contain all the properties the handler may initialize. When registering a defaults handler it is also supported to specify the start class in the inheritance hierarchy of parameters objects on which the handler should be executed. This makes it possible for instance to register a handler for file-based parameters, but define that it should be invoked only for XML parameters. That way special file-related properties can be set for XML configurations, but they will not apply to, say, properties configurations although they are file-based, too. When registering default handlers the registration order matters. Defaults handlers are invoked in the order they have been registered; so a handler registered later can override initializations made by a handler registered before. With these options a very fine-grained control of initialization parameters is possible; especially, different initialization parameters can be set for specific configuration classes even if the parameters are of the same (base) type.

After all this theory let's come to some concrete examples. For now we assume that we already have some DefaultParametersHandler implementations in place that we want to register on a Parameters object. (The next section will focus on the implementation of handlers.) In this example the following is to be achieved:

  • There is a CommonDefaultsHandler class setting default initialization parameters to be applied for all configuration sources.
  • There is a defaults handler for file-based parameters which sets the expected encoding of the file: EncodingDefaultsHandler. We want this handler to be applied on XML configurations only.
  • Our application will also load some properties files. For these configuration sources we want an alternative setting of some basic properties. This is implemented by a handler class called PropertiesDefaultHandler.
The code for this initialization could look as follows:
// Create the defaults handler objects.
DefaultParametersHandler<BasicBuilderParameters> basicHandler =
    new CommonDefaultsHandler();
DefaultParametersHandler<FileBasedBuilderParameters> encodingHandler =
    new EncodingDefaultsHandler("iso-8859-1");
DefaultParametersHandler<PropertiesBuilderParameters> propsHandler =
    new PropertiesDefaultHandler();

// Register the handlers
Parameters params = new Parameters();
params.registerDefaultsHandler(BasicBuilderParameters.class, basicHandler);
params.registerDefaultsHandler(FileBasedBuilderParameters.class,
    encodingHandler, XMLBuilderParameters.class);
params.registerDefaultsHandler(PropertiesBuilderParameters.class, propsHandler);

Now every time this Parameters instance is used for the creation of specific initialization parameters objects, the defaults handlers registered are applied. So the produced parameters objects are already initialized (at least partly). This registration of defaults handlers could be done in the startup phase of an application. The Parameters class is thread-safe, so an application can create and configure a single instance and use it across all modules to create parameter objects. The actual functionality of managing and invoking DefaultParametersHandler objects is implemented by the DefaultParametersManager class - Parameters just delegates to a wrapped instance. In some usage scenarios it may make sense to use DefaultParametersManager directly.

Defining Default Parameters Handlers

After the registration of default handlers has been discussed, it is still open how such handlers can be created. Because the DefaultParametersHandler interface is very simple, it is easy to create a specialized implementation. The following listing shows how the EncodingDefaultsHandler from the previous example could be implemented:

public class EncodingDefaultsHandler
    implements DefaultParametersHandler<FileBasedBuilderParameters>
{
    /** The encoding to be set. */
    private final String encoding;

    /**
     * Creates a new instance and sets the encoding.
     * @param enc the encoding to be set on the parameters objects
     */
    public EncodingDefaultsHandler(String enc)
    {
        encoding = enc;
    }

    @Override
    public void initializeDefaults(FileBasedBuilderParameters parameters)
    {
        parameters.setEncoding(encoding);
    }
}

The point to take is that in the initializeDefaults() method arbitrary initializations can be performed. In many scenarios the implementation of a specialized DefaultParametersHandler is not necessary because Commons Configuration provides a pretty generic default implementation: CopyObjectDefaultHandler. The name stems from the fact that a handler is constructed from a parameters object to be used as reference. In the initializeDefaults() method the handler copies all properties of this reference object onto the object to be initialized. So all a developer needs to do is creating a parameters object of the correct type, initializing all desired properties, and passing this object to a newly created CopyObjectDefaultHandler object. Let's explore how the EncodingDefaultsHandler class discussed previously class can be replaced by CopyObjectDefaultHandler:

Parameters params = new Parameters();

// Create a file-based parameters object to be used as copy source
FileBasedBuilderParameters encParams =
    params.fileBased().setEncoding("iso-8859-1");

// Perform handler registration with a copy handler
params.registerDefaultsHandler(FileBasedBuilderParameters.class,
    new CopyObjectDefaultHandler(encParams), XMLBuilderParameters.class);

So this fragment has the same effect (regarding the initialization of the encoding property) as the example using the custom EncodingDefaultsHandler class - but without the need to provide a custom DefaultParametersHandler implementation. Because of the flexibility of CopyObjectDefaultHandler custom implementations are probably only required for initializations that depend on conditional logic.

This completes the description of the builder concept in Commons Configuration and the BasicConfigurationBuilder base class. Following chapters will deal with specialized builders and explain the extended functionality they provide to the user of this library.