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 }