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