View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration;
18  
19  import java.io.File;
20  import java.net.URL;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.commons.configuration.beanutils.BeanDeclaration;
29  import org.apache.commons.configuration.beanutils.BeanFactory;
30  import org.apache.commons.configuration.beanutils.BeanHelper;
31  import org.apache.commons.configuration.beanutils.DefaultBeanFactory;
32  import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
33  import org.apache.commons.configuration.tree.ConfigurationNode;
34  import org.apache.commons.configuration.tree.DefaultExpressionEngine;
35  import org.apache.commons.configuration.tree.OverrideCombiner;
36  import org.apache.commons.configuration.tree.UnionCombiner;
37  import org.apache.commons.logging.LogFactory;
38  
39  /**
40   * <p>
41   * A factory class that creates a composite configuration from an XML based
42   * <em>configuration definition file</em>.
43   * </p>
44   * <p>
45   * This class provides an easy and flexible means for loading multiple
46   * configuration sources and combining the results into a single configuration
47   * object. The sources to be loaded are defined in an XML document that can
48   * contain certain tags representing the different supported configuration
49   * classes. If such a tag is found, the corresponding <code>Configuration</code>
50   * class is instantiated and initialized using the classes of the
51   * <code>beanutils</code> package (namely
52   * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>
53   * will be used to extract the configuration's initialization parameters, which
54   * allows for complex initialization szenarios).
55   * </p>
56   * <p>
57   * It is also possible to add custom tags to the configuration definition file.
58   * For this purpose register your own <code>ConfigurationProvider</code>
59   * implementation for your tag using the <code>addConfigurationProvider()</code>
60   * method. This provider will then be called when the corresponding custom tag
61   * is detected. For the default configuration classes providers are already
62   * registered.
63   * </p>
64   * <p>
65   * The configuration definition file has the following basic structure:
66   * </p>
67   * <p>
68   *
69   * <pre>
70   * &lt;configuration&gt;
71   *   &lt;header&gt;
72   *     &lt;!-- Optional meta information about the composite configuration --&gt;
73   *   &lt;/header&gt;
74   *   &lt;override&gt;
75   *     &lt;!-- Declarations for override configurations --&gt;
76   *   &lt;/override&gt;
77   *   &lt;additional&gt;
78   *     &lt;!-- Declarations for union configurations --&gt;
79   *   &lt;/additional&gt;
80   * &lt;/configuration&gt;
81   * </pre>
82   *
83   * </p>
84   * <p>
85   * The name of the root element (here <code>configuration</code>) is
86   * arbitrary. There are two sections (both of them are optional) for declaring
87   * <em>override</em> and <em>additional</em> configurations. Configurations
88   * in the former section are evaluated in the order of their declaration, and
89   * properties of configurations declared earlier hide those of configurations
90   * declared later. Configurations in the latter section are combined to a union
91   * configuration, i.e. all of their properties are added to a large hierarchical
92   * configuration. Configuration declarations that occur as direct children of
93   * the root element are treated as override declarations.
94   * </p>
95   * <p>
96   * Each configuration declaration consists of a tag whose name is associated
97   * with a <code>ConfigurationProvider</code>. This can be one of the
98   * predefined tags like <code>properties</code>, or <code>xml</code>, or
99   * a custom tag, for which a configuration provider was registered. Attributes
100  * and sub elements with specific initialization parameters can be added. There
101  * are some reserved attributes with a special meaning that can be used in every
102  * configuration declaration:
103  * </p>
104  * <p>
105  * <table border="1">
106  * <tr>
107  * <th>Attribute</th>
108  * <th>Meaning</th>
109  * </tr>
110  * <tr>
111  * <td valign="top"><code>config-name</code></td>
112  * <td>Allows to specify a name for this configuration. This name can be used
113  * to obtain a reference to the configuration from the resulting combined
114  * configuration (see below).</td>
115  * </tr>
116  * <tr>
117  * <td valign="top"><code>config-at</code></td>
118  * <td>With this attribute an optional prefix can be specified for the
119  * properties of the corresponding configuration.</td>
120  * </tr>
121  * <tr>
122  * <td valign="top"><code>config-optional</code></td>
123  * <td>Declares a configuration as optional. This means that errors that occur
124  * when creating the configuration are ignored. (However
125  * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>s
126  * registered at the builder instance will get notified about this error: they
127  * receive an event of type <code>EVENT_ERR_LOAD_OPTIONAL</code>. The key
128  * property of this event contains the name of the optional configuration source
129  * that caused this problem.)</td>
130  * </tr>
131  * </table>
132  * </p>
133  * <p>
134  * The optional <em>header</em> section can contain some meta data about the
135  * created configuration itself. For instance, it is possible to set further
136  * properties of the <code>NodeCombiner</code> objects used for constructing
137  * the resulting configuration.
138  * </p>
139  * <p>
140  * The configuration object returned by this builder is an instance of the
141  * <code>{@link CombinedConfiguration}</code> class. The return value of the
142  * <code>getConfiguration()</code> method can be casted to this type, and the
143  * <code>getConfiguration(boolean)</code> method directly declares
144  * <code>CombinedConfiguration</code> as return type. This allows for
145  * convenient access to the configuration objects maintained by the combined
146  * configuration (e.g. for updates of single configuration objects). It has also
147  * the advantage that the properties stored in all declared configuration
148  * objects are collected and transformed into a single hierarchical structure,
149  * which can be accessed using different expression engines.
150  * </p>
151  * <p>
152  * All declared override configurations are directly added to the resulting
153  * combined configuration. If they are given names (using the
154  * <code>config-name</code> attribute), they can directly be accessed using
155  * the <code>getConfiguration(String)</code> method of
156  * <code>CombinedConfiguration</code>. The additional configurations are
157  * alltogether added to another combined configuration, which uses a union
158  * combiner. Then this union configuration is added to the resulting combined
159  * configuration under the name defined by the <code>ADDITIONAL_NAME</code>
160  * constant.
161  * </p>
162  * <p>
163  * Implementation note: This class is not thread-safe. Especially the
164  * <code>getConfiguration()</code> methods should be called by a single thread
165  * only.
166  * </p>
167  *
168  * @since 1.3
169  * @author <a
170  * href="http://commons.apache.org/configuration/team-list.html">Commons
171  * Configuration team</a>
172  * @version $Id: DefaultConfigurationBuilder.java 595043 2007-11-14 20:44:43Z oheger $
173  */
174 public class DefaultConfigurationBuilder extends XMLConfiguration implements
175         ConfigurationBuilder
176 {
177     /**
178      * Constant for the name of the additional configuration. If the
179      * configuration definition file contains an <code>additional</code>
180      * section, a special union configuration is created and added under this
181      * name to the resulting combined configuration.
182      */
183     public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
184             .getName()
185             + "/ADDITIONAL_CONFIG";
186 
187     /**
188      * Constant for the type of error events caused by optional configurations
189      * that cannot be loaded.
190      */
191     public static final int EVENT_ERR_LOAD_OPTIONAL = 51;
192 
193     /** Constant for the name of the configuration bean factory. */
194     static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
195             .getName()
196             + ".CONFIG_BEAN_FACTORY_NAME";
197 
198     /** Constant for the reserved name attribute. */
199     static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
200             + XMLBeanDeclaration.RESERVED_PREFIX
201             + "name"
202             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
203 
204     /** Constant for the name of the at attribute. */
205     static final String ATTR_ATNAME = "at";
206 
207     /** Constant for the reserved at attribute. */
208     static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
209             + XMLBeanDeclaration.RESERVED_PREFIX
210             + ATTR_ATNAME
211             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
212 
213     /** Constant for the at attribute without the reserved prefix. */
214     static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
215             + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
216 
217     /** Constant for the name of the optional attribute. */
218     static final String ATTR_OPTIONALNAME = "optional";
219 
220     /** Constant for the reserved optional attribute. */
221     static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
222             + XMLBeanDeclaration.RESERVED_PREFIX
223             + ATTR_OPTIONALNAME
224             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
225 
226     /** Constant for the optional attribute without the reserved prefix. */
227     static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
228             + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
229 
230     /** Constant for the file name attribute. */
231     static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
232             + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
233 
234     /** Constant for the forceCreate attribute. */
235     static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
236             + XMLBeanDeclaration.RESERVED_PREFIX
237             + "forceCreate"
238             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
239 
240     /** Constant for the name of the header section. */
241     static final String SEC_HEADER = "header";
242 
243     /** Constant for an expression that selects the union configurations. */
244     static final String KEY_UNION = "additional";
245 
246     /** An array with the names of top level configuration sections.*/
247     static final String[] CONFIG_SECTIONS = {
248         "additional", "override", SEC_HEADER
249     };
250 
251     /**
252      * Constant for an expression that selects override configurations in the
253      * override section.
254      */
255     static final String KEY_OVERRIDE = "override";
256 
257     /**
258      * Constant for the key that points to the list nodes definition of the
259      * override combiner.
260      */
261     static final String KEY_OVERRIDE_LIST = SEC_HEADER
262             + ".combiner.override.list-nodes.node";
263 
264     /**
265      * Constant for the key that points to the list nodes definition of the
266      * additional combiner.
267      */
268     static final String KEY_ADDITIONAL_LIST = SEC_HEADER
269             + ".combiner.additional.list-nodes.node";
270 
271     /**
272      * Constant for the key of the result declaration. This key can point to a
273      * bean declaration, which defines properties of the resulting combined
274      * configuration.
275      */
276     static final String KEY_RESULT = SEC_HEADER + ".result";
277 
278     /** Constant for the key of the combiner in the result declaration.*/
279     static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
280 
281     /** Constant for the XML file extension. */
282     static final String EXT_XML = ".xml";
283 
284     /** Constant for the provider for properties files. */
285     private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
286             XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
287             EXT_XML);
288 
289     /** Constant for the provider for XML files. */
290     private static final ConfigurationProvider XML_PROVIDER = new XMLConfigurationProvider();
291 
292     /** Constant for the provider for JNDI sources. */
293     private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
294             JNDIConfiguration.class);
295 
296     /** Constant for the provider for system properties. */
297     private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
298             SystemConfiguration.class);
299 
300     /** Constant for the provider for plist files. */
301     private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
302             "org.apache.commons.configuration.plist.XMLPropertyListConfiguration",
303             "org.apache.commons.configuration.plist.PropertyListConfiguration",
304             EXT_XML);
305 
306     /** Constant for the provider for configuration definition files.*/
307     private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
308 
309     /** An array with the names of the default tags. */
310     private static final String[] DEFAULT_TAGS =
311     {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"};
312 
313     /** An array with the providers for the default tags. */
314     private static final ConfigurationProvider[] DEFAULT_PROVIDERS =
315     {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
316             SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER};
317 
318     /**
319      * The serial version UID.
320      */
321     private static final long serialVersionUID = -3113777854714492123L;
322 
323     /** Stores the configuration that is currently constructed.*/
324     private CombinedConfiguration constructedConfiguration;
325 
326     /** Stores a map with the registered configuration providers. */
327     private Map providers;
328 
329     /** Stores the base path to the configuration sources to load. */
330     private String configurationBasePath;
331 
332     /**
333      * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A
334      * configuration definition file is not yet loaded. Use the diverse setter
335      * methods provided by file based configurations to specify the
336      * configuration definition file.
337      */
338     public DefaultConfigurationBuilder()
339     {
340         super();
341         providers = new HashMap();
342         registerDefaultProviders();
343         registerBeanFactory();
344         setLogger(LogFactory.getLog(getClass()));
345         addErrorLogListener();  // log errors per default
346     }
347 
348     /**
349      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
350      * sets the specified configuration definition file.
351      *
352      * @param file the configuration definition file
353      */
354     public DefaultConfigurationBuilder(File file)
355     {
356         this();
357         setFile(file);
358     }
359 
360     /**
361      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
362      * sets the specified configuration definition file.
363      *
364      * @param fileName the name of the configuration definition file
365      * @throws ConfigurationException if an error occurs when the file is loaded
366      */
367     public DefaultConfigurationBuilder(String fileName)
368             throws ConfigurationException
369     {
370         this();
371         setFileName(fileName);
372     }
373 
374     /**
375      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
376      * sets the specified configuration definition file.
377      *
378      * @param url the URL to the configuration definition file
379      * @throws ConfigurationException if an error occurs when the file is loaded
380      */
381     public DefaultConfigurationBuilder(URL url) throws ConfigurationException
382     {
383         this();
384         setURL(url);
385     }
386 
387     /**
388      * Returns the base path for the configuration sources to load. This path is
389      * used to resolve relative paths in the configuration definition file.
390      *
391      * @return the base path for configuration sources
392      */
393     public String getConfigurationBasePath()
394     {
395         return (configurationBasePath != null) ? configurationBasePath
396                 : getBasePath();
397     }
398 
399     /**
400      * Sets the base path for the configuration sources to load. Normally a base
401      * path need not to be set because it is determined by the location of the
402      * configuration definition file to load. All relative pathes in this file
403      * are resolved relative to this file. Setting a base path makes sense if
404      * such relative pathes should be otherwise resolved, e.g. if the
405      * configuration file is loaded from the class path and all sub
406      * configurations it refers to are stored in a special config directory.
407      *
408      * @param configurationBasePath the new base path to set
409      */
410     public void setConfigurationBasePath(String configurationBasePath)
411     {
412         this.configurationBasePath = configurationBasePath;
413     }
414 
415     /**
416      * Adds a configuration provider for the specified tag. Whenever this tag is
417      * encountered in the configuration definition file this provider will be
418      * called to create the configuration object.
419      *
420      * @param tagName the name of the tag in the configuration definition file
421      * @param provider the provider for this tag
422      */
423     public void addConfigurationProvider(String tagName,
424             ConfigurationProvider provider)
425     {
426         if (tagName == null)
427         {
428             throw new IllegalArgumentException("Tag name must not be null!");
429         }
430         if (provider == null)
431         {
432             throw new IllegalArgumentException("Provider must not be null!");
433         }
434 
435         providers.put(tagName, provider);
436     }
437 
438     /**
439      * Removes the configuration provider for the specified tag name.
440      *
441      * @param tagName the tag name
442      * @return the removed configuration provider or <b>null</b> if none was
443      * registered for that tag
444      */
445     public ConfigurationProvider removeConfigurationProvider(String tagName)
446     {
447         return (ConfigurationProvider) providers.remove(tagName);
448     }
449 
450     /**
451      * Returns the configuration provider for the given tag.
452      *
453      * @param tagName the name of the tag
454      * @return the provider that was registered for this tag or <b>null</b> if
455      * there is none
456      */
457     public ConfigurationProvider providerForTag(String tagName)
458     {
459         return (ConfigurationProvider) providers.get(tagName);
460     }
461 
462     /**
463      * Returns the configuration provided by this builder. Loads and parses the
464      * configuration definition file and creates instances for the declared
465      * configurations.
466      *
467      * @return the configuration
468      * @throws ConfigurationException if an error occurs
469      */
470     public Configuration getConfiguration() throws ConfigurationException
471     {
472         return getConfiguration(true);
473     }
474 
475     /**
476      * Returns the configuration provided by this builder. If the boolean
477      * parameter is <b>true</b>, the configuration definition file will be
478      * loaded. It will then be parsed, and instances for the declared
479      * configurations will be created.
480      *
481      * @param load a flag whether the configuration definition file should be
482      * loaded; a value of <b>false</b> would make sense if the file has already
483      * been created or its content was manipulated using some of the property
484      * accessor methods
485      * @return the configuration
486      * @throws ConfigurationException if an error occurs
487      */
488     public CombinedConfiguration getConfiguration(boolean load)
489             throws ConfigurationException
490     {
491         if (load)
492         {
493             load();
494         }
495 
496         CombinedConfiguration result = createResultConfiguration();
497         constructedConfiguration = result;
498 
499         List overrides = fetchTopLevelOverrideConfigs();
500         overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
501         initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
502 
503         List additionals = fetchChildConfigs(KEY_UNION);
504         if (!additionals.isEmpty())
505         {
506             CombinedConfiguration addConfig = new CombinedConfiguration(
507                     new UnionCombiner());
508             result.addConfiguration(addConfig, ADDITIONAL_NAME);
509             initCombinedConfiguration(addConfig, additionals,
510                     KEY_ADDITIONAL_LIST);
511         }
512 
513         return result;
514     }
515 
516     /**
517      * Creates the resulting combined configuration. This method is called by
518      * <code>getConfiguration()</code>. It checks whether the
519      * <code>header</code> section of the configuration definition file
520      * contains a <code>result</code> element. If this is the case, it will be
521      * used to initialize the properties of the newly created configuration
522      * object.
523      *
524      * @return the resulting configuration object
525      * @throws ConfigurationException if an error occurs
526      */
527     protected CombinedConfiguration createResultConfiguration()
528             throws ConfigurationException
529     {
530         XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
531         CombinedConfiguration result = (CombinedConfiguration) BeanHelper
532                 .createBean(decl, CombinedConfiguration.class);
533 
534         if (getMaxIndex(KEY_COMBINER) < 0)
535         {
536             // No combiner defined => set default
537             result.setNodeCombiner(new OverrideCombiner());
538         }
539 
540         return result;
541     }
542 
543     /**
544      * Initializes a combined configuration for the configurations of a specific
545      * section. This method is called for the override and for the additional
546      * section (if it exists).
547      *
548      * @param config the configuration to be initialized
549      * @param containedConfigs the list with the declaratinos of the contained
550      * configurations
551      * @param keyListNodes a list with the declaration of list nodes
552      * @throws ConfigurationException if an error occurs
553      */
554     protected void initCombinedConfiguration(CombinedConfiguration config,
555             List containedConfigs, String keyListNodes) throws ConfigurationException
556     {
557         List listNodes = getList(keyListNodes);
558         for (Iterator it = listNodes.iterator(); it.hasNext();)
559         {
560             config.getNodeCombiner().addListNode((String) it.next());
561         }
562 
563         for (Iterator it = containedConfigs.iterator(); it.hasNext();)
564         {
565             HierarchicalConfiguration conf = (HierarchicalConfiguration) it
566                     .next();
567             ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
568                     conf);
569             AbstractConfiguration newConf = createConfigurationAt(decl);
570             if (newConf != null)
571             {
572                 config.addConfiguration(newConf, decl.getConfiguration()
573                         .getString(ATTR_NAME), decl.getAt());
574             }
575         }
576     }
577 
578     /**
579      * Registers the default configuration providers supported by this class.
580      * This method will be called during initialization. It registers
581      * configuration providers for the tags that are supported by default.
582      */
583     protected void registerDefaultProviders()
584     {
585         for (int i = 0; i < DEFAULT_TAGS.length; i++)
586         {
587             addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
588         }
589     }
590 
591     /**
592      * Performs interpolation. This method will not only take this configuration
593      * instance into account (which is the one that loaded the configuration
594      * definition file), but also the so far constructed combined configuration.
595      * So variables can be used that point to properties that are defined in
596      * configuration sources loaded by this builder.
597      *
598      * @param value the value to be interpolated
599      * @return the interpolated value
600      */
601     protected Object interpolate(Object value)
602     {
603         Object result = super.interpolate(value);
604         if (constructedConfiguration != null)
605         {
606             result = constructedConfiguration.interpolate(result);
607         }
608         return result;
609     }
610 
611     protected void fireError(int type, String propName, Object propValue,
612             Throwable ex)
613     {
614         // This method is only overridden to fix a mysterious MethodNotFound
615         // error in the test cases when run under a JDK 1.3.
616         super.fireError(type, propName, propValue, ex);
617     }
618 
619     /**
620      * Creates a configuration object from the specified configuration
621      * declaration.
622      *
623      * @param decl the configuration declaration
624      * @return the new configuration object
625      * @throws ConfigurationException if an error occurs
626      */
627     private AbstractConfiguration createConfigurationAt(
628             ConfigurationDeclaration decl) throws ConfigurationException
629     {
630         try
631         {
632             return (AbstractConfiguration) BeanHelper.createBean(decl);
633         }
634         catch (Exception ex)
635         {
636             // redirect to configuration exceptions
637             throw new ConfigurationException(ex);
638         }
639     }
640 
641     /**
642      * Returns a list with <code>SubnodeConfiguration</code> objects for the
643      * child nodes of the specified configuration node.
644      *
645      * @param node the start node
646      * @return a list with subnode configurations for the node's children
647      */
648     private List fetchChildConfigs(ConfigurationNode node)
649     {
650         List children = node.getChildren();
651         List result = new ArrayList(children.size());
652         for (Iterator it = children.iterator(); it.hasNext();)
653         {
654             result.add(createSubnodeConfiguration((Node) it.next()));
655         }
656         return result;
657     }
658 
659     /**
660      * Returns a list with <code>SubnodeConfiguration</code> objects for the
661      * child nodes of the node specified by the given key.
662      *
663      * @param key the key (must define exactly one node)
664      * @return a list with subnode configurations for the node's children
665      */
666     private List fetchChildConfigs(String key)
667     {
668         List nodes = fetchNodeList(key);
669         if (nodes.size() > 0)
670         {
671             return fetchChildConfigs((ConfigurationNode) nodes.get(0));
672         }
673         else
674         {
675             return Collections.EMPTY_LIST;
676         }
677     }
678 
679     /**
680      * Finds the override configurations that are defined as top level elements
681      * in the configuration definition file. This method will fetch the child
682      * elements of the root node and remove the nodes that represent other
683      * configuration sections. The remaining nodes are treated as definitions
684      * for override configurations.
685      *
686      * @return a list with subnode configurations for the top level override
687      * configurations
688      */
689     private List fetchTopLevelOverrideConfigs()
690     {
691         List configs = fetchChildConfigs(getRootNode());
692         for (Iterator it = configs.iterator(); it.hasNext();)
693         {
694             String nodeName = ((SubnodeConfiguration) it.next()).getRootNode()
695                     .getName();
696             for (int i = 0; i < CONFIG_SECTIONS.length; i++)
697             {
698                 if (CONFIG_SECTIONS[i].equals(nodeName))
699                 {
700                     it.remove();
701                     break;
702                 }
703             }
704         }
705         return configs;
706     }
707 
708     /**
709      * Registers the bean factory used by this class if necessary. This method
710      * is called by the constructor to ensure that the required bean factory is
711      * available.
712      */
713     private void registerBeanFactory()
714     {
715         synchronized (getClass())
716         {
717             if (!BeanHelper.registeredFactoryNames().contains(
718                     CONFIG_BEAN_FACTORY_NAME))
719             {
720                 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
721                         new ConfigurationBeanFactory());
722             }
723         }
724     }
725 
726     /**
727      * <p>
728      * A base class for creating and initializing configuration sources.
729      * </p>
730      * <p>
731      * Concrete sub classes of this base class are responsible for creating
732      * specific <code>Configuration</code> objects for the tags in the
733      * configuration definition file. The configuration factory will parse the
734      * definition file and try to find a matching
735      * <code>ConfigurationProvider</code> for each encountered tag. This
736      * provider is then asked to create a corresponding
737      * <code>Configuration</code> object. It is up to a concrete
738      * implementation how this object is created and initialized.
739      * </p>
740      * <p>
741      * Note that at the moment only configuration classes derived from
742      * <code>{@link AbstractConfiguration}</code> are supported.
743      * </p>
744      */
745     public static class ConfigurationProvider extends DefaultBeanFactory
746     {
747         /** Stores the class of the configuration to be created. */
748         private Class configurationClass;
749 
750         /** Stores the name of the configuration class to be created.*/
751         private String configurationClassName;
752 
753         /**
754          * Creates a new uninitialized instance of
755          * <code>ConfigurationProvider</code>.
756          */
757         public ConfigurationProvider()
758         {
759             this((Class) null);
760         }
761 
762         /**
763          * Creates a new instance of <code>ConfigurationProvider</code> and
764          * sets the class of the configuration created by this provider.
765          *
766          * @param configClass the configuration class
767          */
768         public ConfigurationProvider(Class configClass)
769         {
770             setConfigurationClass(configClass);
771         }
772 
773         /**
774          * Creates a new instance of <code>ConfigurationProvider</code> and
775          * sets the name of the class of the configuration created by this
776          * provider.
777          *
778          * @param configClassName the name of the configuration class
779          * @since 1.4
780          */
781         public ConfigurationProvider(String configClassName)
782         {
783             setConfigurationClassName(configClassName);
784         }
785 
786         /**
787          * Returns the class of the configuration returned by this provider.
788          *
789          * @return the class of the provided configuration
790          */
791         public Class getConfigurationClass()
792         {
793             return configurationClass;
794         }
795 
796         /**
797          * Sets the class of the configuration returned by this provider.
798          *
799          * @param configurationClass the configuration class
800          */
801         public void setConfigurationClass(Class configurationClass)
802         {
803             this.configurationClass = configurationClass;
804         }
805 
806         /**
807          * Returns the name of the configuration class returned by this
808          * provider.
809          *
810          * @return the configuration class name
811          * @since 1.4
812          */
813         public String getConfigurationClassName()
814         {
815             return configurationClassName;
816         }
817 
818         /**
819          * Sets the name of the configuration class returned by this provider.
820          *
821          * @param configurationClassName the name of the configuration class
822          * @since 1.4
823          */
824         public void setConfigurationClassName(String configurationClassName)
825         {
826             this.configurationClassName = configurationClassName;
827         }
828 
829         /**
830          * Returns the configuration. This method is called to fetch the
831          * configuration from the provider. This implementation will call the
832          * inherited <code>{@link
833          * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
834          * createBean()}</code> method to create a new instance of the
835          * configuration class.
836          *
837          * @param decl the bean declaration with initialization parameters for
838          * the configuration
839          * @return the new configuration object
840          * @throws Exception if an error occurs
841          */
842         public AbstractConfiguration getConfiguration(
843                 ConfigurationDeclaration decl) throws Exception
844         {
845             return (AbstractConfiguration) createBean(fetchConfigurationClass(),
846                     decl, null);
847         }
848 
849         /**
850          * Returns an uninitialized configuration of the represented type. This
851          * method will be called for optional configurations when the
852          * <code>getConfiguration()</code> method caused an error and the
853          * <code>forceCreate</code> attribute is set. A concrete sub class can
854          * here try to create an uninitialized, empty configuration, which may
855          * be possible if the error was created during initialization. This base
856          * implementation just returns <b>null</b>.
857          *
858          * @param decl the bean declaration with initialization parameters for
859          * the configuration
860          * @return the new configuration object
861          * @throws Exception if an error occurs
862          * @since 1.4
863          */
864         public AbstractConfiguration getEmptyConfiguration(
865                 ConfigurationDeclaration decl) throws Exception
866         {
867             return null;
868         }
869 
870         /**
871          * Returns the configuration class supported by this provider. If a
872          * class object was set, it is returned. Otherwise the method tries to
873          * resolve the class name.
874          *
875          * @return the class of the configuration to be created
876          * @since 1.4
877          */
878         protected synchronized Class fetchConfigurationClass() throws Exception
879         {
880             if (getConfigurationClass() == null)
881             {
882                 setConfigurationClass(loadClass(getConfigurationClassName()));
883             }
884             return getConfigurationClass();
885         }
886 
887         /**
888          * Loads the class with the specified name dynamically. If the class's
889          * name is <b>null</b>, <b>null</b> will also be returned.
890          *
891          * @param className the name of the class to be loaded
892          * @return the class object
893          * @throws ClassNotFoundException if class loading fails
894          * @since 1.4
895          */
896         protected Class loadClass(String className)
897                 throws ClassNotFoundException
898         {
899             return (className != null) ? Class.forName(className, true,
900                     getClass().getClassLoader()) : null;
901         }
902     }
903 
904     /**
905      * <p>
906      * A specialized <code>BeanDeclaration</code> implementation that
907      * represents the declaration of a configuration source.
908      * </p>
909      * <p>
910      * Instances of this class are able to extract all information about a
911      * configuration source from the configuration definition file. The
912      * declaration of a configuration source is very similar to a bean
913      * declaration processed by <code>XMLBeanDeclaration</code>. There are
914      * very few differences, e.g. some reserved attributes like
915      * <code>optional</code> and <code>at</code> and the fact that a bean
916      * factory is never needed.
917      * </p>
918      */
919     public static class ConfigurationDeclaration extends XMLBeanDeclaration
920     {
921         /** Stores a reference to the associated configuration builder. */
922         private DefaultConfigurationBuilder configurationBuilder;
923 
924         /**
925          * Creates a new instance of <code>ConfigurationDeclaration</code> and
926          * initializes it.
927          *
928          * @param builder the associated configuration builder
929          * @param config the configuration this declaration is based onto
930          */
931         public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
932                 HierarchicalConfiguration config)
933         {
934             super(config);
935             configurationBuilder = builder;
936         }
937 
938         /**
939          * Returns the associated configuration builder.
940          *
941          * @return the configuration builder
942          */
943         public DefaultConfigurationBuilder getConfigurationBuilder()
944         {
945             return configurationBuilder;
946         }
947 
948         /**
949          * Returns the value of the <code>at</code> attribute.
950          *
951          * @return the value of the <code>at</code> attribute (can be <b>null</b>)
952          */
953         public String getAt()
954         {
955             String result = this.getConfiguration().getString(ATTR_AT_RES);
956             return (result == null) ? this.getConfiguration().getString(ATTR_AT)
957                     : result;
958         }
959 
960         /**
961          * Returns a flag whether this is an optional configuration.
962          *
963          * @return a flag if this declaration points to an optional
964          * configuration
965          */
966         public boolean isOptional()
967         {
968             Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
969                     null);
970             if (value == null)
971             {
972                 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
973                         Boolean.FALSE);
974             }
975             return value.booleanValue();
976         }
977 
978         /**
979          * Returns a flag whether this configuration should always be created
980          * and added to the resulting combined configuration. This flag is
981          * evaluated only for optional configurations whose normal creation has
982          * caused an error. If for such a configuration the
983          * <code>forceCreate</code> attribute is set and the corresponding
984          * configuration provider supports this mode, an empty configuration
985          * will be created and added to the resulting combined configuration.
986          *
987          * @return the value of the <code>forceCreate</code> attribute
988          * @since 1.4
989          */
990         public boolean isForceCreate()
991         {
992             return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
993         }
994 
995         /**
996          * Returns the name of the bean factory. For configuration source
997          * declarations always a reserved factory is used. This factory's name
998          * is returned by this implementation.
999          *
1000          * @return the name of the bean factory
1001          */
1002         public String getBeanFactoryName()
1003         {
1004             return CONFIG_BEAN_FACTORY_NAME;
1005         }
1006 
1007         /**
1008          * Returns the bean's class name. This implementation will always return
1009          * <b>null</b>.
1010          *
1011          * @return the name of the bean's class
1012          */
1013         public String getBeanClassName()
1014         {
1015             return null;
1016         }
1017 
1018         /**
1019          * Checks whether the given node is reserved. This method will take
1020          * further reserved attributes into account
1021          *
1022          * @param nd the node
1023          * @return a flag whether this node is reserved
1024          */
1025         protected boolean isReservedNode(ConfigurationNode nd)
1026         {
1027             if (super.isReservedNode(nd))
1028             {
1029                 return true;
1030             }
1031 
1032             return nd.isAttribute()
1033                     && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1034                             .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1035                             .equals(nd.getName()) && nd.getParentNode()
1036                             .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1037         }
1038 
1039         /**
1040          * Performs interpolation. This implementation will delegate
1041          * interpolation to the configuration builder, which takes care that the
1042          * currently constructed configuration is taken into account, too.
1043          *
1044          * @param value the value to be interpolated
1045          * @return the interpolated value
1046          */
1047         protected Object interpolate(Object value)
1048         {
1049             return getConfigurationBuilder().interpolate(value);
1050         }
1051     }
1052 
1053     /**
1054      * A specialized <code>BeanFactory</code> implementation that handles
1055      * configuration declarations. This class will retrieve the correct
1056      * configuration provider and delegate the task of creating the
1057      * configuration to this object.
1058      */
1059     static class ConfigurationBeanFactory implements BeanFactory
1060     {
1061         /**
1062          * Creates an instance of a bean class. This implementation expects that
1063          * the passed in bean declaration is a declaration for a configuration.
1064          * It will determine the responsible configuration provider and delegate
1065          * the call to this instance. If creation of the configuration fails
1066          * and the <code>optional</code> attribute is set, the exception will
1067          * be ignored. If the <code>forceCreate</code> attribute is set, too,
1068          * the provider is asked to create an empty configuration. A return
1069          * value of <b>null</b> means that no configuration could be created.
1070          *
1071          * @param beanClass the bean class (will be ignored)
1072          * @param data the declaration
1073          * @param param an additional parameter (will be ignored)
1074          * @return the newly created configuration
1075          * @throws Exception if an error occurs
1076          */
1077         public Object createBean(Class beanClass, BeanDeclaration data,
1078                 Object param) throws Exception
1079         {
1080             ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1081             String tagName = decl.getNode().getName();
1082             ConfigurationProvider provider = decl.getConfigurationBuilder()
1083                     .providerForTag(tagName);
1084             if (provider == null)
1085             {
1086                 throw new ConfigurationRuntimeException(
1087                         "No ConfigurationProvider registered for tag "
1088                                 + tagName);
1089             }
1090 
1091             try
1092             {
1093                 return provider.getConfiguration(decl);
1094             }
1095             catch (Exception ex)
1096             {
1097                 // If this is an optional configuration, ignore the exception
1098                 if (!decl.isOptional())
1099                 {
1100                     throw ex;
1101                 }
1102                 else
1103                 {
1104                     // Notify registered error listeners
1105                     decl.getConfigurationBuilder().fireError(
1106                             EVENT_ERR_LOAD_OPTIONAL,
1107                             decl.getConfiguration().getString(ATTR_NAME), null,
1108                             ex);
1109 
1110                     if (decl.isForceCreate())
1111                     {
1112                         try
1113                         {
1114                             return provider.getEmptyConfiguration(decl);
1115                         }
1116                         catch (Exception ex2)
1117                         {
1118                             // Ignore exception, return null in this case
1119                             ;
1120                         }
1121                     }
1122                     return null;
1123                 }
1124             }
1125         }
1126 
1127         /**
1128          * Returns the default class for this bean factory.
1129          *
1130          * @return the default class
1131          */
1132         public Class getDefaultBeanClass()
1133         {
1134             // Here some valid class must be returned, otherwise BeanHelper
1135             // will complain that the bean's class cannot be determined
1136             return Configuration.class;
1137         }
1138     }
1139 
1140     /**
1141      * A specialized provider implementation that deals with file based
1142      * configurations. Ensures that the base path is correctly set and that the
1143      * load() method gets called.
1144      */
1145     public static class FileConfigurationProvider extends ConfigurationProvider
1146     {
1147         /**
1148          * Creates a new instance of <code>FileConfigurationProvider</code>.
1149          */
1150         public FileConfigurationProvider()
1151         {
1152             super();
1153         }
1154 
1155         /**
1156          * Creates a new instance of <code>FileConfigurationProvider</code>
1157          * and sets the configuration class.
1158          *
1159          * @param configClass the class for the configurations to be created
1160          */
1161         public FileConfigurationProvider(Class configClass)
1162         {
1163             super(configClass);
1164         }
1165 
1166         /**
1167          * Creates a new instance of <code>FileConfigurationProvider</code>
1168          * and sets the configuration class name.
1169          *
1170          * @param configClassName the name of the configuration to be created
1171          * @since 1.4
1172          */
1173         public FileConfigurati