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