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