Configuration
Goals
- Recognize the need for storing configuration information.
- Consider different types of configuration information.
- Learn about the Java Preferences API and how to use it.
- Implement a configuration system using the Apache Commons Configuration library.
Concepts
- Apache Commons
- Apache Commons Configuration
- configuration
- configuration file
- hierarchical configuration
- preferences
- string interpolation
- sychronizer
- trigger
- Windows Registry
Library
java.util.NoSuchElementException
java.util.prefs.Preferences
java.util.prefs.Preferences.get(String key, String def)
java.util.prefs.Preferences.getInt(String key, int def)
java.util.prefs.Preferences.node(String pathName)
java.util.prefs.Preferences.put(String key, String value)
java.util.prefs.Preferences.putInt(String key, int value)
java.util.prefs.Preferences.systemNodeForPackage(Class<?> c)
java.util.prefs.Preferences.systemRoot()
java.util.prefs.Preferences.userNodeForPackage(Class<?> c)
java.util.prefs.Preferences.userRoot()
org.apache.commons.configuration2.CompositeConfiguration
org.apache.commons.configuration2.Configuration
org.apache.commons.configuration2.Configuration.addProperty(String key, Object value)
org.apache.commons.configuration2.Configuration.clearProperty(String key)
org.apache.commons.configuration2.Configuration.setProperty(String key, Object value)
org.apache.commons.configuration2.HierarchicalConfiguration<T>
org.apache.commons.configuration2.ImmutableConfiguration
org.apache.commons.configuration2.ImmutableConfiguration.containsKey(String key)
org.apache.commons.configuration2.ImmutableConfiguration.getInt(String key)
org.apache.commons.configuration2.ImmutableConfiguration.getInt(String key, int defaultValue)
org.apache.commons.configuration2.ImmutableConfiguration.getString(String key)
org.apache.commons.configuration2.builder.BasicBuilderParameters
org.apache.commons.configuration2.builder.BasicBuilderParameters.setThrowExceptionOnMissing(boolean b)
org.apache.commons.configuration2.builder.BasicBuilderProperties<T>
org.apache.commons.configuration2.builder.BasicBuilderProperties.setSynchronizer(Synchronizer sync)
org.apache.commons.configuration2.builder.BasicConfigurationBuilder<T extends ImmutableConfiguration>
org.apache.commons.configuration2.builder.BasicConfigurationBuilder.reset()
org.apache.commons.configuration2.builder.BasicConfiguration.configure(BuilderParameters... params)
org.apache.commons.configuration2.builder.BuilderParameters
org.apache.commons.configuration2.builder.ConfigurationBuilder<T extends ImmutableConfiguration>
org.apache.commons.configuration2.builder.ConfigurationBuilder.getConfiguration()
org.apache.commons.configuration2.builder.DatabaseBuilderProperties<T>
org.apache.commons.configuration2.builder.FileBasedBuilderProperties<T>
org.apache.commons.configuration2.builder.FileBasedBuilderProperties.setFile(File file)
org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder<T extends FileBasedConfiguration>
org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder.save()
org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder.setAutoSave(boolean enabled)
org.apache.commons.configuration2.builder.PropertiesBuilderProperties<T>
org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder<T extends FileBasedConfiguration>
org.apache.commons.configuration2.builder.XMLBuilderProperties<T>
org.apache.commons.configuration2.builder.fluent.DatabaseBuilderParameters
org.apache.commons.configuration2.builder.fluent.FileBasedBuilderParameters
org.apache.commons.configuration2.builder.fluent.Parameters
org.apache.commons.configuration2.builder.fluent.Parameters.basic()
org.apache.commons.configuration2.builder.fluent.Parameters.database()
org.apache.commons.configuration2.builder.fluent.Parameters.fileBased()
org.apache.commons.configuration2.builder.fluent.Parameters.properties()
org.apache.commons.configuration2.builder.fluent.Parameters.xml()
org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters
org.apache.commons.configuration2.builder.fluent.XMLBuilderParameters
org.apache.commons.configuration2.ex.ConfigurationException
org.apache.commons.configuration2.reloading.FileHandlerReloadingDetector
org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger
org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger.shutdown()
org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger.start()
org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger.stop()
org.apache.commons.configuration2.reloading.ReloadingController
org.apache.commons.configuration2.reloading.ReloadingDetector
org.apache.commons.configuration2.sync.ReadWriteSynchronizer
org.apache.commons.configuration2.sync.Synchronizer
Dependencies
Lesson
A well-written application is flexible and can be customized to the needs of its users. The location where an application stores its data files, the name of a user, and formatting options are all examples of things that are commonly configurable within an application.
Selected configuration options are best stored in a separate location from the code—after all, it would hardly be user-friendly to require the software to be recompiled and packaged each time a configuration option were changed. Instead configured values are commonly placed in a separate configuration file in the user's file system. Typical formats include XML, which you've studied, and Java Properties File Format, which you'll learn about in a future lesson.
The term preferences
is also used frequently when talking about a program's configuration. In many cases preferences
refers to the user's preferred options such as a color scheme, which are likely changeable; while configuration
refers to the program's installation options, which may not be alterable by the user. In practice the line between the two can be hard to draw, and sometimes the terms are used interchangeably.
Java Preferences API
Java addressed need for a cross-platform approach to storing program configuration information when it added the Preferences API to the JDK. A program saves and/or retrieves named values from the Preferences API without regard to where the values are actually stored in the system. Preferences are arranged in a hierarchy of nodes
, which is simply a way to categorize preferences similar to how the Java package system works.
Preferences Nodes
The main access to Java preferences is through the java.util.prefs.Preferences
abstract class, which represents a preferences node. Each value is associated with a key, making a Preferences
node similar to a map. To store a string value, call Preferences.put(String key, String value)
. The Preferences.get(String key, String def)
method returns the string value associated with a key. There are also preference storage and retrieval methods for specific types, such Preferences.putInt(String key, int value)
and Preferences.getInt(String key, int def)
which access a preference stored represented by an int
value.
System and User Preferences Trees
Java Preferences are divided into two hierarchical trees of preference nodes: system preferences, which apply to all users; and per-user preferences. To locate a preferences node in which to store information, first start with the root node of one of these two trees using Preferences.systemRoot()
or Preferences.userRoot()
, respectively.
A particular Preferences
node can be located by providing its path, which functions similarly to path names in the file system. Each component represents a Preferences
node, with the forward slash /
character used as a separator. Calling Preferences.node(String pathName)
with a relative path will resolve the path to that of the Preferences
instance on which node(…)
was called. An absolute path will be resolved to the root Preferences
node. The named node will be created if it doesn't already exist.
Apache Commons Configuration
On the other end of the spectrum, from simple to complex, lies the other established API for accessing configuration information: Apache Commons Configuration. The Apache Software Foundation has a large group of general utility libraries that form part of the Apache Commons. The Apache Commons Configuration library is just one of many projects in this group; others include Apache Commons Collections, Apache Commons Imaging, and Apache Commons Math.
Many of the Apache Commons projects are large, interconnected libraries that have evolved comprehensive solutions. Apache Commons Configuration is no exception: it comes with support for storing configuration information in many formats, including XML. Version 2 of the library has recently been completely rewritten to be more modular and flexible.
Configuration
Regardless of the underlying storage format for configuration information, the main interface for access to configuration values is org.apache.commons.configuration2.Configuration
. This interface allows reading configuration data as well as setting new configuration values. All methods related solely to retrieving values are found in the Configuration
parent interface org.apache.commons.configuration2.ImmutableConfiguration
. Retrieving a string value for example is performed using ImmutableConfiguration.getString(String key)
. Access to other types of data is achieved using methods such as ImmutableConfiguration.getInt(String key)
. You can also determine if a value exists for a key using ImmutableConfiguration.containsKey(String key)
.
ImmutableConfiguration
methods that retrieve objects will return null
if a requested value is not available. For primitive types, a java.util.NoSuchElementException
is thrown if a value is missing. This issue can be sidestepped altogether by using one of the method variations that allow the designation of a default value, such as ImmutableConfiguration.getInt(String key, int defaultValue)
.
ConfigurationBuilder
While it is possible to manually create and configure the Configuration
subclass appropriate for your configuration file format, Apache Commons Configuration provides the org.apache.commons.configuration2.builder.ConfigurationBuilder<T extends ImmutableConfiguration>
interface for provides a specification for the Configuration
you would like to create. ConfigurationBuilder
is not simply a throwaway builder like the builder design pattern you've seen in the past. Instead it functions more like the factory design pattern; once you configure a ConfigurationBuilder
as you like it, you use it to get a Configuration
instance whenever you need one using the ConfigurationBuilder.getConfiguration()
method.
There are many concrete ConfigurationBuilder
subclasses that relate to the specific type of configuration source desired. Here are a few of the most common ones:
org.apache.commons.configuration2.builder.BasicConfigurationBuilder<T extends ImmutableConfiguration>
- Base
ConfigurationBuilder
implementation; provides theBasicConfiguration.configure(BuilderParameters... params)
method. org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder<T extends FileBasedConfiguration>
- Subclass of
BasicConfigurationBuilder
for creating configurations based on file content such as XML. org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder<T extends FileBasedConfiguration>
- Specialized
FileBasedConfigurationBuilder
that allows reloading upon detection of configuration file changes.
BuilderParameters
A ConfigurationBuilder
must itself be configured before use, and this is done using an instance of the org.apache.commons.configuration2.builder.BuilderParameters
interface. As with ConfigurationBuilder
, there is a hierarchy of BuilderParameters
implementations and interfaces, such as:
org.apache.commons.configuration2.builder.BasicBuilderParameters
- The base class for other
BuilderParameter
implementations using configuration properties defined inorg.apache.commons.configuration2.builder.BasicBuilderProperties<T>
. org.apache.commons.configuration2.builder.fluent.FileBasedBuilderParameters
- Interfaces for configuring file-based configuration builders using configuration properties defined in
org.apache.commons.configuration2.builder.FileBasedBuilderProperties<T>
. org.apache.commons.configuration2.builder.fluent.XMLBuilderParameters
- Configures access to a configuration stored in an XML file using configuration properties defined in
org.apache.commons.configuration2.builder.XMLBuilderProperties<T>
. org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters
- Configures access to a configuration stored in a Java properties file using configuration properties defined in
org.apache.commons.configuration2.builder.PropertiesBuilderProperties<T>
. You will learn about Java properties files in an upcoming lesson. org.apache.commons.configuration2.builder.fluent.DatabaseBuilderParameters
- Configures access to a configuration stored in a database using configuration properties defined in
org.apache.commons.configuration2.builder.DatabaseBuilderProperties<T>
.
Many of the BuilderParameters
types listed above are interfaces; actual configuration of the parameters requires some concrete implementation. The implementations of BuilderParameters
use a fluent API, allowing chaining of parameter-setting calls as shown in the example below.
Parameters
Creating and access BuilderParameter
instances is made slightly less easier through use of the org.apache.commons.configuration2.builder.fluent.Parameters
utility class. Parameters
has various factory methods that produce instances of the BuilderParameters
interfaces discussed above, including:
Parameters.basic()
Parameters.fileBased()
Parameters.xml()
Parameters.properties()
Parameters.database()
The following example shows how to create a simple configuration stored in XML format.
XML Configurations
A configuration based on an XML files is a hierarchical configuration, supporting a tree-like hierarchy of configuration keys. In Apache Commons Configuration these types of configurations implement org.apache.commons.configuration2.HierarchicalConfiguration<T>
, which allows access of subtrees of configuration nodes within a configuration file. Hierarchical configurations can be used like basic configurations, using the full stop .
character to indicate levels in the hierarchy.
XML configurations reflect the hierarchical nature of XML elements. Consider the XML file used in a previous lesson to illustrate storing information about a vehicle. The content of element nodes such <make>
can be accessed in the Apache Commons Configuration API by indicating all the XML element hierarchy names separated by the full stop .
character, or in this case "type.make"
. Note that XML configurations ignore the root element of the hierarchy, in this case the document element <car>
.
Interpolation
By default the configuration implementations support string interpolation by automatically replacing placeholders in configuration values that appear in the form ${variable}
. Normally the variable is another configuration key, allowing you to create configuration values made up of other configuration values. For example you might make the name of a car contain the car's make and model using interpolation:
Apache Commons Configurations support several prefixes that indicate variables from contexts other than the current configuration
Prefix | Context | Example |
---|---|---|
sys | System properties. | ${sys:user.home} |
const | Constant (i.e. static final ) variables in the form com.example.package.Class.CONSTANT . | ${const:java.lang.Math.PI} |
env | Operating system environment variables. | ${env:JAVA_HOME} |
Modifying Configurations
The most common use case for a configuration framework is to retrieve static configuration data that has been prepared for the application. But in some situations, such as when you allow a user to configure parts of the application, you may want to update configuration values and save them back to the disk. As already mentioned the Configuration interface is mutable, allowing you to change a value using Configuration.setProperty(String key, Object value)
or add a property using Configuration.addProperty(String key, Object value)
. You can even remove a property altogether using Configuration.clearProperty(String key)
.
After modifying a configuration you will likely want to persist it to disk. If you have followed the recommendations above, you will have a separate configuration builder which serves as a configuration factory. File-based configuration builders also serve as as a persistence mechanism for your configuration via the FileBasedConfigurationBuilder.save()
method.
Automatic Reloading
Most often an application will load its configuration once and assume that it has complete control over the configuration data until the program ends. Sometimes a program will want to detect if the configuration file has been modified externally, either by a user or another program, and reload the configuration data when it changes in the file system. This can be configured using the ReloadingFileBasedConfigurationBuilder
mentioned above.
A ReloadingFileBasedConfigurationBuilder
contains a org.apache.commons.configuration2.reloading.ReloadingController
which governs the process of checking for file changes and clearing the configuration for reloading when appropriate. Internally the ReloadingController
uses a org.apache.commons.configuration2.reloading.ReloadingDetector
strategy for determining when reloading should take place. The most commonly used ReloadingDetector
is a org.apache.commons.configuration2.reloading.FileHandlerReloadingDetector
, which detects that a configuration file needs reloading based on a change in its last-modified timestamp.
Apache Commons Configuration uses a trigger that determines when a ReloadingController
should ask its ReloadingDetector
to check for configuration changes. The library comes with a org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger
which uses a background service to trigger the ReloadingController
every so often. You can start and stop this activity using PeriodicReloadingTrigger.start()
and PeriodicReloadingTrigger.stop()
, respectively.
Review
Gotchas
- The default Java Preferences storage is not guaranteed to be available, so you probably don't want to store critical data using this framework.
- Turning on auto-save in Apache Commons Configuration can cause unnecessary I/O activity if you change many configuration values at a time.
In the Real World
- It is not uncommon for an application to use several
configuration
files:- An internal, unchanging configuration file bundled with the application for program configuration.
- Internal, unchanging
resources
for displaying information in other language. You will learn about internationalization in an upcoming lesson. - An external, modifiable configuration for user settings.
- Another external, modifiable configuration for non-critical user preferences.
Think About It
- Is
configuration builder
the best name for the Apache Commons Configuration class that produces configurations? - Determine the best type of configuration file to use.
- Are the values set for the entire deployment of the application? Use a static, unchanging configuration file distributed with the application.
- Is the user allowed to modify the configuration to control the program functionality? Store the values in a separate configuration file somewhere in the user directory tree.
- Are the values non-critical user-preference settings such as window layouts and list sorting orders? Consider placing these in a separate
preferences
configuration.
- In your opinion is there a true distinction between
configuration
values andpreferences
settings
Self Evaluation
- What are the benefits of the Java Preferences API? What are some of its disadvantages?
- Rather than creating a single
Configuration
instance, what is the recommended way to access configuration information in Apache Common Configuration? - What is
string interpolation
and what part does it play in the Apache Common Configuration library?
Task
When Booker starts, in addition to printing the current date and time, print the number of years, days, hours, and minutes since the last time the program was run.
- Use the Java Preferences API to store the last run time.
- If a larger time component is zero, don't include it. For example, if the last execution time was two minutes ago, there is no need to include years, days, or hours.
- If the program has never been run, don't print anything.
In a previous lesson you stored the Booker user name in an XML configuration file, parsing it manually using the XML DOM. Switch to using the Apache Commons Configuration library to load this same configuration file, and read the user information using the appropriate Apache Commons Configuration API calls.
See Also
- Existing Configuration Solutions (Anatole Tresch)
- Comparison of configuration libraries for Java (nort.pl)
- Preferences API Overview (Oracle Java™ Platform Overview)
- Advanced Java tutorial - The Preferences Class (YouTube - Hector Domingo)
References
- Core Java Preferences API (Oracle Java™ Platform Overview)
- Apache Commons Configuration User Guide
- Apache Commons Configuration 2.1 API
Resources
Acknowledgments
- Apache Commons, Apache Commons Configuration, Apache, the Apache feather logo, and the Apache Commons project logos are trademarks of The Apache Software Foundation.
- Some symbols are from Font Awesome by Dave Gandy.