View Javadoc

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