Apache Commons logo Commons Configuration

Properties files

Properties files are a popular means of configuring applications. Of course, Commons Configuration supports this format and enhances significantly the basic java.util.Properties class. This section introduces the features of the PropertiesConfiguration class. Note that PropertiesConfiguration is a very typical example for an implementation of the Configuration interface; it extends AbstractConfiguration, thus all the features provided by this base class are available here as well. More information about functionality common to all standard Configuration implementations can be found in the section Basic features and AbstractConfiguration.

Using PropertiesConfiguration

Let's start with a simple properties file named usergui.properties with the following content:

# Properties definining the GUI
colors.background = #FFFFFF
colors.foreground = #000080

window.width = 500
window.height = 300

To load this file, you'll write something like:

Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
    new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
    .configure(params.properties()
        .setFileName("usergui.properties"));
try
{
    Configuration config = builder.getConfiguration();
    ...
}
catch(ConfigurationException cex)
{
    // loading of the configuration file failed
}

As is demonstrated by this example, a configuration object for a properties file is obtained via a FileBasedConfigurationBuilder as described in the section File-based Configurations.

After the properties file was loaded you can access its content through the methods of the Configuration interface, e.g.

String backColor = config.getString("colors.background");
Dimension size = new Dimension(config.getInt("window.width"),
  config.getInt("window.height"));

Includes

If a property is named "include", and the value of that property is the name of a file on the disk, that file will be included into the configuration.

The difference between "include" and "includeOptional" (below) is that if the file value is absent, processing continues with "includeOptional" but stops with "include".

For example:

# usergui.properties

include = colors.properties
include = sizes.properties
# colors.properties

colors.background = #FFFFFF

Oprional Includes

If a property is named "includeOptional", and the value of that property is the name of a file on the disk, that file will be included into the configuration.

The difference between "include" (above) and "includeOptional" is that if the file value is absent, processing continues with "includeOptional" but stops with "include".

For example:

# usergui.properties

includeOptional = colors.properties
includeOptional = sizes.properties
# colors.properties

colors.background = #FFFFFF

Lists and arrays

As was already pointed out in the section List handling of Basic features, Commons Configuration has the ability to return easily a list of values. For example, a properties file can contain a list of comma separated values:

# chart colors
colors.pie = #FF0000, #00FF00, #0000FF

Provided that an appropriate ListDelimiterHandler object was set for the configuration instance, the value is split automatically, and you can retrieve an array or a java.util.List directly with:

String[] colors = config.getStringArray("colors.pie");
List<Object> colorList = config.getList("colors.pie");

Splitting of string values at list delimiter characters is disabled by default. It can be enabled by specifying an instance of DefaultListDelimiterHandler. This can be done when loading the configuration via the builder:

Parameters params = new Parameters();
FileBasedConfigurationBuilder<Configuration> builder =
    new FileBasedConfigurationBuilder<Configuration>(PropertiesConfiguration.class)
    .configure(params.properties()
        .setFileName("usergui.properties")
        .setListDelimiterHandler(new DefaultListDelimiterHandler(','));
Configuration config = builder.getConfiguration();

Alternatively, you can specify a list of values in your properties file by using the same key on several lines as shown in the following example. This is an example of a feature not provided by java.util.Properties:

# chart colors
colors.pie = #FF0000;
colors.pie = #00FF00;
colors.pie = #0000FF;

All of the features related to list handling described for AbstractConfiguration also apply to properties files, including changing the list delimiter or disabling list handling at all.

Saving

To save your configuration, just call the save() method on the associated configuration builder.

Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
    new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
    .configure(params.properties()
        .setFileName("usergui.properties")
        .setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
Configuration config = builder.getConfiguration();
config.setProperty("colors.background", "#000000");
builder.save();

More information about saving properties files (and file-based configurations in general) can be found in the section about File-based Configurations.

Special Characters and Escaping

If you need a special character in a property like a line feed, a tabulation or an unicode character, you can specify it with the same escaped notation used for Java Strings. The list separator ("," by default), can also be escaped:

key = This \n string \t contains \, escaped \\ characters \u0020

When dealing with lists of elements that contain backslash characters (e.g. file paths on Windows systems) escaping rules can become pretty complex. The first thing to keep in mind is that in order to get a single backslash, you have to write two:

config.dir = C:\\Temp\\
      

This issue is not specific to Commons Configuration, but is related to the standard format for properties files. Refer to the Javadocs of the load() method of java.util.Properties for more information. Now if you want to define a list with file paths, you may be tempted to write the following:

# Wrong way to define a list of directories
config.dirs = C:\\Temp\\,D:\\data\\
      

As the comment indicates, this will not work. The trailing backslash of the first directory is interpreted as escape character for the list delimiter. So instead of a list with two elements only a single value of the property is defined - clearly not what was desired. To get a correct list the trailing backslash has to be escaped. This is achieved by duplicating it (yes, in a properties file that means that we now need 4 backslashes):

# Correct way to define a list of directories
config.dirs = C:\\Temp\\\\,D:\\data\\
      

So a sequence of 4 backslashes in the value of a property is interpreted as an escaped backslash and eventually results in a single backslash. This creates another problem when a properties file should refer to the names of network shares. Typically these names start with two backslashes, so the obvious way to define such a property is as follows:

# Wrong way to define a list of network shares
config.dirs = \\\\share1,\\\\share2
      

Unfortunately, this will not work because the shares contain the reserved sequence of 4 backslashes. So when reading the value of the config.dirs property a list with two elements is returned starting only with a single backslash. To fix the problem the sequence for escaping a backslash has to be duplicated - we are now at 8 backslashes:

# Correct way to define a list of network shares
config.dirs = \\\\\\\\share1,\\\\\\\\share2
      

As becomes obvious, escape sequences can become pretty complex and unreadable. In such situations it is recommended to use the alternative way of defining a list: just use the same key multiple times. In this case no additional escaping of backslashes (beyond the usual duplicating required by properties files) is needed because there is no list delimiter character involved. Using this syntax the list of network shares looks like the following:

# Straightforward way to define a list of network shares
config.dirs = \\\\share1
config.dirs = \\\\share2
      

Please also refer to the Javadocs of the DefaultListDelimiterHandler class; it describes the escaping rules to be applied in detail.

Layout Objects

Each PropertiesConfiguration object is associated with a Layout object, an instance of the class PropertiesConfigurationLayout. This layout object is responsible for preserving most of the structure of loaded configuration files. This means that things like comments or blank lines in a saved properties file will closely resemble the original properties file (the algorithm is not 100 percent perfect, but for most use cases it should be sufficient).

Normally a developer does not have to deal with these layout objects. However, there are some methods that might be of interest if enhanced control over the output of properties files is needed. The following list describes these methods (note that corresponding get methods are of course also provided):

  • setComment()
    With this method a comment can be set for a specified property. When storing the configuration the comment is output before the property, followed by a line break. The comment can span multiple lines; in this case the newline character "\n" must be used as line separator.
  • setHeaderComment()
    With setHeaderComment() a global comment can be set for the properties file. This comment is written at the very start of the file, followed by an empty line.
  • setFooterComment()
    Analogous to setHeaderComment(), but the comment defined by this method is written at the very end of the properties file.
  • setBlancLinesBefore()
    This methods allows defining the number of empty lines to be written before the specified property. It can be used, for instance, to divide the properties file into multiple logical sections.
  • setSingleLine()
    If a property has multiple values, with setSingleLine() it can be specified that all these values should be written into a single line separated by the default list separator. It is also possible to write multiple definitions for this property (i.e. multiple lines of the form property = value1, property = value2 etc.). This is supported by PropertiesConfiguration, but will probably not work when processing the properties file with other tools.
  • setForceSingleLine()
    This is similar to setSingleLine(), but sets a global single line flag. If set to true, all properties with multiple values are always written on a single line.
  • setGlobalSeparator()
    Sometimes it may be necessary to define the properties separator, i.e. the string that separates the property key from the value. This can be done using setGlobalSeparator(). Here an arbitrary string can be specified that will be used as separator. (Note: In order to produce valid properties files only the characters = and : should be used as separators (with or without leading or trailing whitespace), but the method does not enforce this.
  • setSeparator()
    This method is similar to setGlobalSeparator(), but allows setting the property separator for a specific property.
  • setLineSeparator()
    Using this method the line separator can be specified. Per default the platform-specific line separator is used (e.g. \n on Unix).
The default settings of PropertiesConfigurationLayout are chosen in a way that most of the original layout of a properties file is retained. With the methods listed above specific layout restrictions can be enforced.

Custom properties readers and writers

There are situations when more control over the process of reading and writing properties files is needed. For instance, an application might have to deal with some legacy properties file in a specific format, which is not supported out of the box by PropertiesConfiguration, but must not be modified. In these cases it is possible to inject a custom reader and writer for properties files.

Per default properties files are read and written by the nested classes PropertiesReader and PropertiesWriter (defined within PropertiesConfiguration). These classes are regular reader and writer classes (both are derived from typical base classes of the java.io package) that provide some additional methods making dealing with properties files more convenient. Custom implementations of properties readers and writers must extend these base classes.

For installing a custom properties reader or writer PropertiesConfiguration provides the IOFactory interface (which is also defined as a nested class). An object implementing this interface is stored in each PropertiesConfiguration instance. Whenever a properties file is to be read or written (i.e. when one of the load() or save() methods is called), the IOFactory object is asked for creating the properties reader or writer to be used.

The IOFactory interface is pretty simple; it defines one method for creating a properties reader and another one for creating a properties writer. A default implementation called DefaultIOFactory is also available and is used by PropertiesConfiguration when no specific IOFactory is set.
The original goal of PropertiesConfiguration wasn't to be strictly compatible with the exact file format defined in java.util.Properties (JUP). However, in cases where this compatibility is required, the alternative JupIOFactory can be used, as it aims to mimic the exact behavior of JUP. The main differences concern the handling of leading and trailing whitespace and the handling of escape sequences. JupIOFactory can also be configured to avoid Unicode escape sequences (like \u00DF) when the used encoding already supports all characters natively. E.g. UTF-8 is the new default encoding for resource bundle properties files since Java 9, so Unicode escapes are not required anymore and not using them can make properties files much more readable in regular text editors.

To make this discussion more concrete we provide an example of how to inject a custom properties reader. The use case is that we have to load a properties file that contains keys with whitespace, which is not supported by PropertiesConfiguration per default. A fragment from such a properties file could look as follows:

Background Color = #800080
Foreground Color = #000080

The first step is to create a custom properties reader implementation that can deal with such properties. The class is derived from PropertiesConfiguration.PropertiesReader and overrides the parseProperty() method:

public class WhitespacePropertiesReader extends PropertiesConfiguration.PropertiesReader
{
    public WhitespacePropertiesReader(Reader in, char delimiter)
    {
        super(in, delimiter);
    }

    /**
     * Special algorithm for parsing properties keys with whitespace. This
     * method is called for each non-comment line read from the properties
     * file.
     */
    @Override
    protected void parseProperty(String line)
    {
        // simply split the line at the first '=' character
        // (this should be more robust in production code)
        int pos = line.indexOf('=');
        String key = line.substring(0, pos).trim();
        String value = line.substring(pos + 1).trim();

        // now store the key and the value of the property
        initPropertyName(key);
        initPropertyValue(value);
    }
}

Notice the calls to the methods initPropertyName() and initPropertyValue(). Here the results of the parsing operation are stored. The next step is to provide a specialized implementation of the IOFactory interface that returns the new properties reader class. As we only want to replace the properties reader (and use the standard writer), we can derive our implementation from DefaultIOFactory and thus only have to override the createPropertiesReader() method.

public class WhitespaceIOFactory extends PropertiesConfiguration.DefaultIOFactory
{
    /**
     * Return our special properties reader.
     */
     @Override
    public PropertiesReader createPropertiesReader(Reader in, char delimiter)
    {
        return new WhitespacePropertiesReader(in, delimiter);
    }
}

Finally an instance of our new IOFactory implementation has to be created and passed to the PropertiesConfiguration object. This can be done via the initialization parameters passed to the configuration builder:

Parameters params = new Parameters();
FileBasedConfigurationBuilder<Configuration> builder =
    new FileBasedConfigurationBuilder<Configuration>(PropertiesConfiguration.class)
    .configure(params.properties()
        .setFileName("myfile.properties")
        .setIOFactory(new WhitespaceIOFactory());
Configuration config = builder.getConfiguration();

Builder Configuration Related to Properties Files

When setting up a configuration builder to produce a PropertiesConfiguration instance typically an object implementing the PropertiesBuilderParameters interface is used. In addition to the parameters common to all file-based configurations, there are settings specific to properties files which are defined by the PropertiesBuilderProperties interface. These include

  • A flag whether include files are supported. This is true by default, but can be switched off if properties named include should not have a special meaning.
  • A custom layout object.
  • A custom I/O factory.

A parameters object for a properties configuration can be obtained using the properties() method of a Parameters instance