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