blob: 043702f24d20086ea5c92d802a3bbbf0957e4d50 [file] [log] [blame]
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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>Combined Configurations</title>
<author email="oheger@apache.com">Oliver Heger</author>
</properties>
<body>
<section name="Combined Configuration">
<p>
The <code><a href="apidocs/org/apache/commons/configuration/CombinedConfiguration.html">
CombinedConfiguration</a></code> class provides an alternative for handling
multiple configuration sources. Its API is very similar to the
<code>CompositeConfiguration</code> class, which was discussed in the
<a href="howto_compositeconfiguration.html#Composite Configuration Details">last
section</a>. There are the following differences however:
</p>
<p>
<ul>
<li>A <code>CombinedConfiguration</code> is a truely
<a href="howto_xml.html#Hierarchical properties">hierarchical
configuration</a>. This means that all the enhanced facilities
provided by <code>HierarchicalConfiguration</code> (e.g. expression
engines) can be used.</li>
<li>A <code>CombinedConfiguration</code> is not limited to implementing
an override semantics for the properties of the contained configurations.
Instead it has the concept of so-called <em>node combiners</em>, which
know how properties of multiple configuration sources can be combined.
Node combiners are discussed later in detail. For instance, there is a
node combiner implementation available that constructs a union of the
contained configurations.</li>
<li>Contained configurations can be assigned a name. They can later be
accessed by their name.</li>
<li>Each contained configuration can have an optional prefix. Its
properties are then added under this prefix to the combined
configuration.</li>
<li>There is no concept of an <em>in memory configuration</em>. Changes
to a combined configuration are handled in a different way.</li>
</ul>
</p>
<subsection name="How it works">
<p>
A <code>CombinedConfiguration</code> provides a logic view on the
properties of the configurations it contains. This view is determined
by the associated node combiner object. Because of that it must be
re-constructed whenever one of these contained configurations is
changed.
</p>
<p>
To achieve this, a <code>CombinedConfiguration</code> object registers
itself as an event listener at the configurations that are added to it.
It will then be notified for every modification that occurs. If such a
notification is received, the internally managed view is invalidated.
When a property of the combined configuration is to be accessed, the view
is checked whether it is valid. If this is the case, the property's value
can be directly fetched. Otherwise the associated node combiner is asked
to re-construct the view.
</p>
</subsection>
<subsection name="Node combiners">
<p>
A <em>node combiner</em> is an object of a class that inherits from the
abstract <code><a href="apidocs/org/apache/commons/configuration/tree/NodeCombiner.html">NodeCombiner</a></code>
class. This class defines an abstract <code>combine()</code> method, which
takes the root nodes of two hierarchical configurations and returns the
root node of the combined node structure. It is up to a concrete
implementation how this combined structure will look like. Commons
Configuration ships with the two concrete implementations
<code><a href="apidocs/org/apache/commons/configuration/tree/OverrideCombiner.html">OverrideCombiner</a></code>
and <code><a href="apidocs/org/apache/commons/configuration/tree/UnionCombiner.html">UnionCombiner</a></code>,
which implement an override and a union semantics respective.
</p>
<p>
Constructing a combination of multiple node hierarchies is not a trivial
task. The available implementations descend the passed in node hierarchies
in a recursive manner to decide, which nodes have to be copied into the
resulting structure. Under certain circumstances two nodes of the source
structures can be combined into a single result node, but unfortunately
this process cannot be fully automated, but sometimes needs some hints
from the developer. As an example consider the following XML configuration
sources:
</p>
<source><![CDATA[
<configuration>
<database>
<tables>
<table>
<name>users</name>
<fields>
<field>
<name>user_id</name>
</field>
...
</fields>
</table>
</tables>
</database>
</configuration>
]]></source>
<p>and</p>
<source><![CDATA[
<configuration>
<database>
<tables>
<table>
<name>documents</name>
<fields>
<field>
<name>document_id</name>
</field>
...
</fields>
</table>
</tables>
</database>
</configuration>
]]></source>
<p>
These two configuration sources define database tables. Each source
defines one table. When constructing a union for these sources the result
should look as follows:
</p>
<source><![CDATA[
<configuration>
<database>
<tables>
<table>
<name>users</name>
<fields>
<field>
<name>user_id</name>
</field>
...
</fields>
</table>
<table>
<name>documents</name>
<fields>
<field>
<name>document_id</name>
</field>
...
</fields>
</table>
</tables>
</database>
</configuration>
]]></source>
<p>
As you can see, the resulting structure contains two <code>table</code>
nodes while the nodes <code>database</code> and <code>tables</code> appear
only once. For a human being this is quite logic because <code>database</code>
and <code>tables</code> define the overall structure of the configuration
data, and there can be multiple tables. A node combiner however does not
know anything about structure nodes, list nodes, or whatever. From its point of
view there is no detectable difference between the <code>tables</code>
nodes and the <code>table</code> nodes in the source structures: both
appear once in each source file and have no values. So without any
assistance the result constructed by the <code>UnionCombiner</code> when
applied on the two example sources would be a bit different:
</p>
<source><![CDATA[
<configuration>
<database>
<tables>
<table>
<name>users</name>
<fields>
<field>
<name>user_id</name>
</field>
...
</fields>
<name>documents</name>
<fields>
<field>
<name>document_id</name>
</field>
...
</fields>
</table>
</tables>
</database>
</configuration>
]]></source>
<p>
Note that the <code>table</code> node would be considered a structure
node, too, and would not be duplicated. This is probably not what was
desired. To deal with such situations it is possible to tell the node
combiner that certain nodes are list nodes and thus should not be
combined. So in this concrete example the <code>table</code> node should
be declared as a list node, then we would get the expected result. We will
see below how this is done. Note that this explicit declaration of a list
node is necessary only in situations where there is ambiguity. If in one
of our example configuration sources multiple tables had been defined, the
node combiner would have concluded itself that <code>table</code> is a list
node and would have acted correspondigly.
</p>
</subsection>
<subsection name="Constructing a CombinedConfiguration">
<p>
To create a <code>CombinedConfiguration</code> object you specify the node
combiner to use and then add an arbitrary number of configurations. We will
show how to construct a union configuration from the two example sources
introduced earlier:
</p>
<source><![CDATA[
// Load the source configurations
XMLConfiguration conf1 = new XMLConfiguration("table1.xml");
XMLConfiguration conf2 = new XMLConfiguration("table2.xml");
// Create and initialize the node combiner
NodeCombiner combiner = new UnionCombiner();
combiner.addListNode("table"); // mark table as list node
// this is needed only if there are ambiguities
// Construct the combined configuration
CombinedConfiguration cc = new CombinedConfiguration(combiner);
cc.addConfiguration(conf1, "tab1");
cc.addConfiguration(conf2);
]]></source>
<p>
Here we also specified a name for one of the configurations, so it can
later be accessed by <code>cc.getConfiguration("tab1");</code>. Access by
index is also supported. After that the properties in the combined
configuration can be accessed as if it were a normal hierarchical
configuration
</p>
</subsection>
<subsection name="Dealing with changes">
<p>
There is nothing that prevents you from updating a combined configuration,
e.g. by calling methods like <code>addProperty()</code> or
<code>removeProperty()</code>. The problem is that depending on the used
node combiner it might no be clear, which of the contained configurations
will be modified or whether one is modified at all.
</p>
<p>
Typical node combiners work by copying parts of the node structures of
the source configurations into the target structure and linking them
togehter using special link nodes. So updates of the combined node structure
will either effect nodes from one of the contained configuration (then
the changes are directly visible in this configuration) or one of the link
nodes (then they cannot really be saved).
</p>
<p>
It is also possible that a change is done at the combined node structure,
which is not compatible with the current node combiner. Imagine that an
<code>OverrideCombiner</code> is used and that a
property should be removed. This property will then be removed from one
of the contained configurations. Now it may happen that this removed
property had hidden property values of other contained configurations.
Their values won't become visible automatically, but only after the
combined view was re-constructed.
</p>
<p>
Because of that it is recommended that changes are not done at the
combined configuration, but only at contained configurations. This way
the correct configuration to be updated can unambigously be identified.
Obtaining the configuration to be updated from the combined configuration
is easy when it was given a name.
</p>
</subsection>
</section>
</body>
</document>