When working with file-based configurations application code has multiple ways to specify the location of the file to be loaded (refer to File-based Configurations). If a URL is provided, the source file to be loaded is defined in a pretty unambiguous way. If relative file names or paths are used, situation is less obvious.
Commons Configuration provides two mechanisms to customize the way configuration files are accessed:
Every builder for a file-based configuration is associated with an object of the FileHandler class. A FileHandler combines information about a file (its location plus some meta data like the encoding) with an object that can actually read or write the file. This is typically a configuration object. FileHandler defines methods for setting and querying properties related to the referenced file. For instance, the location can be set in various ways, e.g. as a URL, as a java.io.File object, as a file path, etc. In addition, there are methods for loading and saving the referenced file. These methods evaluate the stored location information and open a stream to the underlying file. Then they delegate to the associated configuration object to actually perform the IO operation.
A FileHandler object is also used under the hood by the parameters object of a file-based configuration builder (see FileBasedBuilderProperties). So an application defining the configuration file to be loaded actually populates the properties of a FileHandler instance. The FileHandler also supports properties which influence the way the file is loaded; the already mentioned FileSystem and FileLocationStrategy properties can be set as well. This is also the most straight-forward way of hooking into the mechanism of accessing a configuration file - by passing customized FileHandler or FileLocationStrategy objects to a parameters object for a file-based configuration builder.
Another important class related to file access is FileLocator. An instance stores all information required for resolving a file to be accessed. FileHandler uses a FileLocator instance to maintain this part of file-related information. If you need to customize the access to configuration files, you sometimes have to deal with FileLocator objects because the files to be operated on are described in terms of such objects.
In its default mode of operation Commons Configuration supports retrieving and storing configuration files either on a local file system or via http. However, Commons Configuration provides support for allowing other File System adapters. All file access is accomplished through the FileSystem class so accessing files using other mechanisms is possible.
Commons Configuration also provides a second FileSystem implementation which allows retrieval using Apache Commons VFS. As of this writing Commons VFS supports 18 protocols for manipulating files.
As was already mentioned in the previous section, the FileSystem used by Commons Configuration can be set in the builder's parameter object, together with other properties defining the file to be loaded. When working with CombinedConfigurationBuilder it is also possible to define the file system in the configuration definition file to be processed by the builder - in both a global way and for each referenced sub configuration. The following listing shows a configuration definition file for a combined builder making use of this functionality. Per default, the VFSFileSystem is used, but the included XML configuration is loaded via a DefaultFileSystem instance:
<configuration> <header> <fileSystem config-class="org.apache.commons.configuration.io.VFSFileSystem"/> </header> <override> <xml fileName="settings.xml" config-name="xml"> <fileSystem config-class="org.apache.commons.configuration.io.DefaultFileSystem"/> </xml> <!-- Other sources omitted --> </override> </configuration>
Commons VFS allows options to the underlying file systems being used. Commons Configuration allows applications to provide these by implementing the FileOptionsProvider interface and registering the provider with the FileSystem. FileOptionsProvider has a single method that must be implemented, getOptions(), which returns a Map containing the keys and values that the FileSystem might use. The getOptions() method is called as each configuration uses VFS to create a FileOjbect to access the file. The map returned does not have to contain the same keys and/or values each time it is called. For example, the value of the currentUser key can be set to the id of the currently logged in user to allow a WebDAV save to record the userid as a file attribute.
Before a file can be accessed it has to be located first. In the 1.x versions of Commons Configuration, there was a hard-coded algorithm for looking up configuration files defined by a file name and an optional base path in various places. Starting with version 2.0, it is now possible to adapt this algorithm. The key to this is the FileLocationStrategy interface. The interface defines a single method:
URL locate(FileSystem fileSystem, FileLocator locator);
The purpose of this method is to resolve a file described by the passed in FileLocator object and return a URL for it. If required, the provided FileSystem can be used. The URL yielded by a successful locate operation is directly used to access the affected file. If the file could not be resolved, a FileLocationStrategy implementation should not throw an exception, but return null instead. This allows multiple strategies to be chained so that different locations can be searched for the file one after the other.
Commons Configuration ships with a set of standard FileLocationStrategy implementations. They are pretty specialized, meaning that a single implementation focuses on a very specific search algorithm. The true power lies in combining these strategies in a way suitable for an application or use case. The following table describes the available FileLocationStrategy implementations:
As an example, consider that an application wants configuration files to be looked up (in this order)
List<FileLocationStrategy> subs = Arrays.asList( new ProvidedURLLocationStrategy(), new FileSystemLocationStrategy(), new ClasspathLocationStrategy()); FileLocationStrategy strategy = new CombinedLocationStrategy(subs);
This strategy can now be passed to a file-based configuration builder. If no strategy is passed to a builder, a default one is used. This default strategy is almost identical to the hard-coded search algorithm that was used in earlier versions of Commons Configuration. In fact, the pre-defined basic FileLocationStrategy implementations were extracted from this algorithm.
Because the FileLocationStrategy interface is very simple it should be easy to create a custom implementation. The specific search algorithm just has to be coded into the locate() method. Then this custom strategy implementation can be combined with other standard strategies by making use of a CombinedLocationStrategy.