blob: 31364c2d45f1d8f88d5d1df036d6e554550e3d15 [file] [log] [blame]
<?xml version="1.0"?>
<!--
Copyright 2004-2005 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<document>
<properties>
<title>Configuration Factory and Hierarchical Structured Data Howto</title>
<author email="oliver.heger@t-online.de">Oliver Heger</author>
</properties>
<body>
<section name="Using a Configuration Factory">
<p>
This section explains how a
<code>ConfigurationFactory</code> object is setup that provides access
to a collection of different configuration sources. It also discusses using Hierarchical
and Structured datasets.
</p>
<subsection name="The configuration definition file">
<p>
When a single configuration file is the only
source of configuration data it is very simple to
load it using a <code>PropertiesConfiguration</code> object
(this is the class that handles files of this type). But because
we think that later other sources will be added (otherwise
this example section would be too silly) we will use a
<code>ConfigurationFactory</code> object to load it.
</p>
<p>
<code>ConfigurationFactory</code> allows to combine
multiple configuration sources. The properties defined in these
sources can then be accessed as if they were defined in a
single configuration file. To make use of this we have to
create a XML file which tells the factory from which sources
the properties are to be collected. The following listing shows
the content of this file:
</p>
<source><![CDATA[
<?xml version="1.0" encoding="ISO-8859-1" ?>
<configuration>
<properties fileName="usergui.properties"/>
</configuration>
]]></source>
<p>
Definition files for <code>ConfigurationFactory</code> are
normal XML files. The root element must be named
<code>configuration</code>. It can contain different sub
elements that specify the configuration sources to load. The
<code>properties</code> element is one of these; it is used to
include properties files.
</p>
<p>
For this example we store the definition file for
<code>ConfigurationFactory</code> in the same directory as the
properties file and call it <code>config.xml</code>.
</p>
</subsection>
<subsection name="Setting up a ConfigurationFactory">
<p>
Now we have to create a <code>ConfigurationFactory</code>
object and let it read this definition file. This is quite simple:
Just create a new instance and set the name of the definition
file with the <code>setConfigurationFileName()</code> method.
</p>
<source><![CDATA[
ConfigurationFactory factory = new ConfigurationFactory();
URL configURL = new File("config.xml").toURL();
factory.setConfigurationFileName(configURL.toString());
Configuration config = factory.getConfiguration();
]]></source>
<p>
As this code fragment shows the file name passed to the factory
can be a full URL. This is also the recommended way of
specifying the file because it provides the greatest flexibility
and a consistent way of handling relative file names found in
the definition file.
</p>
<p>
Here we assumed the configuration definition file to be located
in the current directory. It is also possible (and probably a
better approach) to load the file from the class path. This
could be done as follows:
</p>
<source><![CDATA[
ConfigurationFactory factory = new ConfigurationFactory();
URL configURL = getClass().getResource("/config.xml");
factory.setConfigurationURL(configURL);
Configuration config = factory.getConfiguration();
]]></source>
</subsection>
<subsection name="Accessing properties">
<p>
Whatever way we used to load the configuration factory, we
should now have a <code>Configuration</code> object that was
returned by the factory's <code>getConfiguration()</code>
method. This object defines a large amount of methods for
querying properties. The most generic one is
<code>getProperty()</code>, which returns an object, but there
are lots of other methods that return other datatypes. In our
example the property we have defined has a string value, so we
would use the <code>getString()</code> method.
</p>
<p>
All of these methods have in common that they expect a property
key as argument. Here the name of the searched property must
be provided in exact the same way as it is contained in the
properties file. To obtain the value of the background color
property that is defined in the properties file shown earlier
the following code fragment can be used:
</p>
<source><![CDATA[
String backColor = config.getString("color.background");
]]></source>
</subsection>
</section>
<section name="Multiple configuration sources">
<p>
Using <code>ConfigurationFactory</code> to collect configuration
sources does not make much sense if there is only one source to be
loaded. So let's add another one! This time we will embedd a XML file.
</p>
<subsection name="A XML configuration file">
<p>
Many applications use the popular XML format for storing
configuration information. So it is no wonder that Configuration
also supports this type of configuration sources. In general
each XML document can be used to define configuration settings.
We start here with a rather simple one:
</p>
<source><![CDATA[
<?xml version="1.0" encoding="ISO-8859-1" ?>
<gui-definition>
<colors>
<background>#808080</background>
<text>#000000</text>
<header>#008000</header>
<link normal="#000080" visited="#800080"/>
</colors>
<rowsPerPage>15</rowsPerPage>
</gui-definition>
]]></source>
<p>
(As becomes obvious, this tutorial does not bother with good
design of XML documents, the example file should rather
demonstrate the different ways of accessing properties.)
This XML document should be stored under the name
<code>gui.xml</code> in the same directory as the so far
created configuration files.
</p>
</subsection>
<subsection name="Overriding properties">
<p>
To make this XML document part of our global configuration we
have to modify our configuration definition file to also include
the new file. For XML documents the element <code>xml</code>
can be used so that we have now:
</p>
<source><![CDATA[
<?xml version="1.0" encoding="ISO-8859-1" ?>
<configuration>
<properties fileName="usergui.properties"/>
<xml fileName="gui.xml"/>
</configuration>
]]></source>
<p>
The code for setting up the <code>ConfigurationFactory</code>
object remains the same. The following fragment shows how the
new properties can be accessed:
</p>
<source><![CDATA[
String backColor = config.getString("color.background");
String textColor = config.getString("color.text");
String linkNormal = config.getString("color.link[@normal]");
int rowsPerPage = config.getInt("rowsPerPage");
]]></source>
<p>
This listing demonstrates some important points of constructing
keys for accessing properties load from XML documents:
<ul>
<li>
Nested elements are accessed using a dot notation. In
the example document there is an element
<code>&lt;text&gt;</code> in the body of the
<code>&lt;color&gt;</code> element. The corresponding
key is <code>color.text</code>.
</li>
<li>
The root element is ignored when constructing keys. In
the example you do not write
<code>gui-definition.color.text</code>, but only
<code>color.text</code>.
</li>
<li>
Attributes of XML elements are accessed in a XPath like
notation.
</li>
</ul>
</p>
<p>
There is one problem with the example code fragement: It queries
the value of the <code>color.background</code> property, but
this is defined in both the properties and the XML file and -
to make things worse - with different values. Which value will
be returned by the corresponding call to <code>getString()</code>?
</p>
<p>
The answer is that the configuration sources are searched in the
order they are defined in the configuration definition file.
Here the properties file is included first, then comes the XML
file. Because the <code>color.background</code> property can
be found in the properties file the value specified there will
be returned (which happens to be <code>#FFFFFF</code>).
</p>
<p>
It might not be obvious why it makes sense to define the value
of one and the same property in multiple configuration sources.
But consider the following scenario: An application comes with
a set of default properties and allows the user to override some
or all of them. This can now easy be realized by saving the
user's settings in a file and the default settings in another.
Then in the configuration definition file the file with the
user settings is included first and after that the file with the
default values. The application code that queries these
settings need not be aware whether a property was overriden by
the user. The <code>ConfigurationFactory</code> takes care
that properties defined in the first file (the user file) are
found; other properties which the user has not changed will
still be returned from the second file (the defaults file).
</p>
</subsection>
<subsection name="Optional configuration sources">
<p>
The example above with two configuration sources - one for user
settings and one with default values - raises an interesting
question: What will happen if the user has not defined specific
properties yet? Or what if a new user starts our application for
the first time and thus no user specific properties exist?
</p>
<p>
The default behavior of <code>ConfigurationFactory</code> is to
throw a <code>ConfigurationException</code> exception if one of
the sources defined in the configuration definition file cannot
be loaded. For our example this behavior is not desired: the
properties file with specific user settings is not required. If it
cannot be loaded, the example application will still work because
a complete set of configuration properties is defined in the
second file.
</p>
<p>
<code>ConfigurationFactory</code> supports such optional
configuration sources. For this purpose in the definition of a
(file based) configuration source the <code>optional</code>
attribute can be placed. An example of this is shown below:
</p>
<source><![CDATA[
<?xml version="1.0" encoding="ISO-8859-1" ?>
<configuration>
<properties fileName="usersettings.properties" optional="true"/>
<properties fileName="default.properties"/>
</configuration>
]]></source>
<p>
In this configuration definition file the first properties file
with user specific settings is marked as optional. This means that
if it cannot be loaded, <code>ConfigurationFactory</code> will
not throw an exception, but only write a warning message to its
logger. Note that the <code>optional</code> attribute is absent
for the second properties file. Thus it is mandatory, and the
<code>getConfiguration()</code> method of
<code>ConfigurationFactory</code> would throw an exception if it
could not be found.
</p>
</subsection>
</section>
</body>
</document>