Apache Commons logo Commons Configuration

Utility classes and Tips and Tricks

In this section some utility classes will be introduced that can be used to make handling of configuration objects easier. These classes already provide solutions for some often occurring problems. We will list these problems in no specific order and show how they can be solved with classes provided by Commons Configuration.

Copy a configuration

Often it is required to copy the data of one Configuration object into another one. For this purpose the AbstractConfiguration class (which serves as the base class for most of the configuration implementations shipped with this library) provides two methods implementing a basic copy operation:

  • append() takes the configuration to be copied as argument and adds all of its properties to the current configuration.
  • copy() is very similar to append(). The difference is that properties that already exist in the target configuration are replaced by the properties of the source configuration.

These methods work fine if the target configuration is not a hierarchical configuration. If a hierarchical configuration is to be copied into another one, the methods are not able to handle the hierarchical structure; so the resulting configuration will contain all of the properties of the source configuration, but the specific parent-child relations will probably be lost. If a hierarchical configuration needs to be copied, there are the following options:

  • The clone() method can be used to create a copy of a hierarchical configuration. This also works for non-hierarchical configurations. Most of the configuration implementations provided by Commons Configurations support cloning. The cloneConfiguration() method of ConfigurationUtils can be used for creating a copy of an arbitrary Configuration object. This method checks whether the passed in configuration implements the Cloneable interface and, if so, invokes its clone() method.
  • Most hierarchical configurations have a constructor, which takes another hierarchical configuration as argument. This constructor copies the content of the specified configuration into the newly created object.

Converting a flat configuration into a hierarchical one

Hierarchical configurations provide some enhanced features that are not available for "flat" configurations. For instance, they support more sophisticated query facilities. Because of that it may be sometimes useful to transform an ordinary configuration into a hierarchical one. The following code fragment shows how this can be done:

// Create a flat configuration
Parameters params = new Parameters();
FileBasedConfigurationBuilder<PropertiesConfiguration> builder =
    new FileBasedConfigurationBuilder<PropertiesConfiguration>(PropertiesConfiguration.class)
    .configure(params.fileBased()
        .setFile(new File("config.properties"));
PropertiesConfiguration flatConfig = builder.getConfiguration();
HierarchicalConfiguration<?> hc =
  ConfigurationUtils.convertToHierarchical(flatConfig);

The convertToHierarchical() method of ConfigurationUtils checks whether the passed in object is already a hierarchical configuration. If this is the case, it is returned unchanged. Otherwise, a new instance of BaseHierarchicalConfiguration is created, and the properties of the source configuration are copied into it.

Sometimes a flat configuration contains keys with special characters that are not compatible with the expression engine of a hierarchical configuration. For instance, a properties configuration could have the following property:

test(xy)=true

When processing this property during conversion the default expression engine of the resulting hierarchical configuration will interpret the brackets as an index marker and try to convert the string between the brackets into a number. In this example this fails with a NumberFormatException! The cause for this problem is that the property key contains characters with a special meaning for the default expression engine.

To solve this problem, it is possible to specify an alternative expression engine that will be used for the conversion. For instance, if you know that your property keys can contain brackets, you could use an instance of DefaultExpressionEngine that is configured with a different index marker. This could look as follows:

DefaultExpressionEngineSymbols symbols =
    new DefaultExpressionEngineSymbols.Builder(
        DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS)
        .setIndexStart("[")
        .setIndexEnd("]")
        .create();
DefaultExpressionEngine engineConvert = new DefaultExpressionEngine(symbols);
HierarchicalConfiguration<?> hc =
  ConfigurationUtils.convertToHierarchical(flatConfig, engineConvert);

In this example an expression engine is constructed that uses square brackets as index markers. Therefore, normal brackets do not have a special meaning and thus do not cause problems during conversion any more.

Note: When using a CombinedConfiguration flat configurations contained in the combined configuration are also converted into hierarchical configurations using the methods discussed here. The CombinedConfiguration class defines the method setConversionExpressionEngine(), which can be called to specify an expression engine to be used during this conversion. The expression engine passed to this method will be propagated to ConfigurationUtils.convertToHierarchical().

Converting between properties and configurations

When working with the JDK the java.util.Properties class is typically used for storing configuration data. If Commons Configuration is to be integrated in such an application, there may be the requirement of converting from Properties objects to Configuration objects and vice versa. For this purpose an utility class can be used: ConfigurationConverter.

Usage of this class is pretty simple. It provides some static utility methods that perform different conversions. Below you can see some examples. In this fragment we assume that we have a method processConfiguration() that is called from older parts of an application that are not aware of the Commons Configuration API. So they pass in a Properties object and expect one as return value. Inside the method a temporary Configuration object is created and used.

/**
 * Does some processing of properties.
 * @param props the source properties
 * @return the processed properties
 */
Properties processConfiguration(Properties props)
{
    // Create a configuration for the properties for easy access
    Configuration config = ConfigurationConverter.getConfiguration(props);

    // Now use the Configuration API for manipulating the configuration data
    ...

    // Return a Properties object with the results
    return ConfigurationConverter.getProperties(config);
}

Please refer to the Javadocs of ConfigurationConverter to learn more about the available conversion methods and their limitations.

Interpolation of all variables

Another issue with the integration of Commons Configuration with native Java applications can be variables: Configuration implementations are able to detect variables like ${myReference} or ${sys:java.version} in the values of their properties and substitute them by their current values (see the section Variable Interpolation for more details). External components probably do not know how to handle such placeholders when processing configuration files written by Commons Configuration.

AbstractConfiguration provides the method interpolatedConfiguration(). This method creates a clone of the current configuration and then performs interpolation on all of its properties. So the result of this method is a configuration object with basically the same content as the original configuration, but with all variables replaced by their actual values (as far as this was possible). The following code fragment shows how a PropertiesConfiguration object can be saved in a way that the resulting properties file does not contain any variables:

// Load a properties file (which may contain variables)
Parameters params = new Parameters();
FileBasedConfigurationBuilder<PropertiesConfiguration> builder =
    new FileBasedConfigurationBuilder<PropertiesConfiguration>(PropertiesConfiguration.class)
    .configure(params.fileBased()
        .setFile(new File("config.properties"));
PropertiesConfiguration config = builder.getConfiguration();

// Perform interpolation on all variables
PropertiesConfiguration extConfig =
    (PropertiesConfiguration) config.interpolatedConfiguration();

// Save the interpolated configuration (no more variables)
FileHandler handler = new FileHandler(extConfig);
handler.save("external_config.properties");

Handling of runtime exceptions

Section Configuration Error Events introduces a way of dealing with runtime exceptions that can occur on accessing configuration properties by registering an event listener. If you do not want to provide a special error handler, but only need to propagate the exception that caused the error event, you can make use of a convenience method of the ConfigurationUtils class: enableRuntimeExceptions() registers a special error listener at the passed in configuration that throws a ConfigurationRuntimeException exception for each received error event. The following code fragment shows an example of using this method:

JNDIConfiguration config = new JNDIConfiguration();
ConfigurationUtils.enableRuntimeExceptions(config);

// This may now throw a ConfigurationRuntimeException
String value = config.getString("myKey");

enableRuntimeExceptions() can be called for all Configuration implementations that implement the EventSource interface (which is the case for almost all configuration classes provided by this library). Of course, the affected implementation must support the mechanism of error events, otherwise the registered listener will not be triggered. In Configuration Error Events more information can be found.

Wrapping Configuration Builders

Section Creating Configurations describes the concept of configuration builders as the preferred way of creating configurations. The full flexibility of configuration builders can be leveraged if an application holds a central builder instance, and each time access to configuration information is needed, this builder is queried for its managed Configuration object. This approach enables dynamism; the builder is free to replace its managed configuration, for instance, when it detects a change on an external configuration source. This is fully transparent for the application.

The concept of configuration builders has been introduced in version 2.0 of Commons Configuration. In older versions of this library Configuration objects were the only source of configuration data. So applications using this version probably pass around objects of this type. This makes the migration to the new configuration builders harder.

There is one helper class which can simplify this migration: BuilderConfigurationWrapperFactory. This class is capable of creating special proxy objects implementing the Configuration interface which behind the scenes delegate to the managed configuration object of a configuration builder. For instance, if a wrapper configuration named config has been created for the configuration builder builder, a call like config.getString("key") is translated to

builder.getConfiguration().getString("key");

In order to create such a proxy wrapping a configuration builder, an instance of BuilderConfigurationWrapperFactory is needed. The instance can be configured with an enumeration constant of the EventSourceSupport class which determines whether and how the proxy objects created by this factory should implement the EventSource interface. Then proxy objects can be constructed by calling the createBuilderConfigurationWrapper() method passing in the interface class and the configuration builder instance to be wrapped. The following code fragment shows how a wrapper configuration for a configuration builder can be produced:

// Create the builder to be wrapped
Parameters params = new Parameters();
FileBasedConfigurationBuilder<Configuration> builder =
    new FileBasedConfigurationBuilder<Configuration>(PropertiesConfiguration.class)
    .configure(params.fileBased()
        .setFile(new File("config.properties"));

// Create the wrapping proxy
BuilderConfigurationWrapperFactory wrapperFactory =
    new BuilderConfigurationWrapperFactory(BuilderConfigurationWrapperFactory.EventSourceSupport.BUILDER);
Configuration config = wrapperFactory.createBuilderConfigurationWrapper(
    Configuration.class, builder);