    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}
    059     * class is instantiated and initialized using the classes of the
    060     * {@code beanutils} package (namely
    061     * {@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}
    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}
    068     * implementation for your tag using the {@code addConfigurationProvider()}
    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}) 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}. This can be one of the
    113     * predefined tags like {@code properties}, or {@code xml}, or
    114     * a custom tag, for which a configuration provider was registered. Attributes
    115     * and sub elements with specific initialization parameters can be added. There
    116     * are some reserved attributes with a special meaning that can be used in every
    117     * configuration declaration:
    118     * </p>
    119     * <p>
    120     * <table border="1">
    121     * <tr>
    122     * <th>Attribute</th>
    123     * <th>Meaning</th>
    124     * </tr>
    125     * <tr>
    126     * <td valign="top">{@code config-name}</td>
    127     * <td>Allows to specify a name for this configuration. This name can be used
    128     * to obtain a reference to the configuration from the resulting combined
    129     * configuration (see below).</td>
    130     * </tr>
    131     * <tr>
    132     * <td valign="top">{@code config-at}</td>
    133     * <td>With this attribute an optional prefix can be specified for the
    134     * properties of the corresponding configuration.</td>
    135     * </tr>
    136     * <tr>
    137     * <td valign="top">{@code config-optional}</td>
    138     * <td>Declares a configuration as optional. This means that errors that occur
    139     * when creating the configuration are ignored. (However
    140     * {@link org.apache.commons.configuration.event.ConfigurationErrorListener}s
    141     * registered at the builder instance will get notified about this error: they
    142     * receive an event of type {@code EVENT_ERR_LOAD_OPTIONAL}. The key
    143     * property of this event contains the name of the optional configuration source
    144     * that caused this problem.)</td>
    145     * </tr>
    146     * </table>
    147     * </p>
    148     * <p>
    149     * The optional <em>header</em> section can contain some meta data about the
    150     * created configuration itself. For instance, it is possible to set further
    151     * properties of the {@code NodeCombiner} objects used for constructing
    152     * the resulting configuration.
    153     * </p>
    154     * <p>
    155     * The default configuration object returned by this builder is an instance of the
    156     * {@link CombinedConfiguration} class. The return value of the
    157     * {@code getConfiguration()} method can be casted to this type, and the
    158     * {@code getConfiguration(boolean)} method directly declares
    159     * {@code CombinedConfiguration} as return type. This allows for
    160     * convenient access to the configuration objects maintained by the combined
    161     * configuration (e.g. for updates of single configuration objects). It has also
    162     * the advantage that the properties stored in all declared configuration
    163     * objects are collected and transformed into a single hierarchical structure,
    164     * which can be accessed using different expression engines. The actual CombinedConfiguration
    165     * implementation can be overridden by specifying the class in the <em>config-class</em>
    166     * attribute of the result element.
    167     * </p>
    168     * <p>
    169     * A custom EntityResolver can be used for all XMLConfigurations by adding
    170     * <pre>
    171     * &lt;entity-resolver config-class="EntityResolver fully qualified class name"&gt;
    172     * </pre>
    173     * The CatalogResolver can be used for all XMLConfiguration by adding
    174     * <pre>
    175     * &lt;entity-resolver catalogFiles="comma separated list of catalog files"&gt;
    176     * </pre>
    177     * </p>
    178     * <p>
    179     * Additional ConfigurationProviders can be added by configuring them in the <em>header</em>
    180     * section.
    181     * <pre>
    182     * &lt;providers&gt;
    183     *   &lt;provider config-tag="tag name" config-class="provider fully qualified class name"/&gt;
    184     * &lt;/providers&gt;
    185     * </pre>
    186     * </p>
    187     * <p>
    188     * Additional variable resolvers can be added by configuring them in the <em>header</em>
    189     * section.
    190     * <pre>
    191     * &lt;lookups&gt;
    192     *   &lt;lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/&gt;
    193     * &lt;/lookups&gt;
    194     * </pre>
    195     * </p>
    196     * <p>
    197     * All declared override configurations are directly added to the resulting
    198     * combined configuration. If they are given names (using the
    199     * {@code config-name} attribute), they can directly be accessed using
    200     * the {@code getConfiguration(String)} method of
    201     * {@code CombinedConfiguration}. The additional configurations are
    202     * altogether added to another combined configuration, which uses a union
    203     * combiner. Then this union configuration is added to the resulting combined
    204     * configuration under the name defined by the {@code ADDITIONAL_NAME}
    205     * constant.
    206     * </p>
    207     * <p>
    208     * Implementation note: This class is not thread-safe. Especially the
    209     * {@code getConfiguration()} methods should be called by a single thread
    210     * only.
    211     * </p>
    212     *
    213     * @since 1.3
    214     * @author <a
    215     * href="http://commons.apache.org/configuration/team-list.html">Commons
    216     * Configuration team</a>
    217     * @version $Id: DefaultConfigurationBuilder.java 1208782 2011-11-30 21:12:00Z oheger $
    218     */
    219    public class DefaultConfigurationBuilder extends XMLConfiguration implements
    220            ConfigurationBuilder
    221    {
    222        /**
    223         * Constant for the name of the additional configuration. If the
    224         * configuration definition file contains an {@code additional}
    225         * section, a special union configuration is created and added under this
    226         * name to the resulting combined configuration.
    227         */
    228        public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
    229                .getName()
    230                + "/ADDITIONAL_CONFIG";
    231
    232        /**
    233         * Constant for the type of error events caused by optional configurations
    234         * that cannot be loaded.
    235         */
    236        public static final int EVENT_ERR_LOAD_OPTIONAL = 51;
    237
    238        /** Constant for the name of the configuration bean factory. */
    239        static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
    240                .getName()
    241                + ".CONFIG_BEAN_FACTORY_NAME";
    242
    243        /** Constant for the reserved name attribute. */
    244        static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    245                + XMLBeanDeclaration.RESERVED_PREFIX
    246                + "name"
    247                + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    248
    249        /** Constant for the name of the at attribute. */
    250        static final String ATTR_ATNAME = "at";
    251
    252        /** Constant for the reserved at attribute. */
    253        static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    254                + XMLBeanDeclaration.RESERVED_PREFIX
    255                + ATTR_ATNAME
    256                + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    257
    258        /** Constant for the at attribute without the reserved prefix. */
    259        static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    260                + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    261
    262        /** Constant for the name of the optional attribute. */
    263        static final String ATTR_OPTIONALNAME = "optional";
    264
    265        /** Constant for the reserved optional attribute. */
    266        static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    267                + XMLBeanDeclaration.RESERVED_PREFIX
    268                + ATTR_OPTIONALNAME
    269                + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    270
    271        /** Constant for the optional attribute without the reserved prefix. */
    272        static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    273                + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    274
    275        /** Constant for the file name attribute. */
    276        static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    277                + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    278
    279        /** Constant for the forceCreate attribute. */
    280        static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
    281                + XMLBeanDeclaration.RESERVED_PREFIX
    282                + "forceCreate"
    283                + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
    284
    285        /**
    286         * Constant for the tag attribute for providers.
    287         */
    288        static final String KEY_SYSTEM_PROPS = "[@systemProperties]";
    289
    290        /** Constant for the name of the header section. */
    291        static final String SEC_HEADER = "header";
    292
    293        /** Constant for an expression that selects the union configurations. */
    294        static final String KEY_UNION = "additional";
    295
    296        /** An array with the names of top level configuration sections.*/
    297        static final String[] CONFIG_SECTIONS = {
    298            "additional", "override", SEC_HEADER
    299        };
    300
    301        /**
    302         * Constant for an expression that selects override configurations in the
    303         * override section.
    304         */
    305        static final String KEY_OVERRIDE = "override";
    306
    307        /**
    308         * Constant for the key that points to the list nodes definition of the
    309         * override combiner.
    310         */
    311        static final String KEY_OVERRIDE_LIST = SEC_HEADER
    312                + ".combiner.override.list-nodes.node";
    313
    314        /**
    315         * Constant for the key that points to the list nodes definition of the
    316         * additional combiner.
    317         */
    318        static final String KEY_ADDITIONAL_LIST = SEC_HEADER
    319                + ".combiner.additional.list-nodes.node";
    320
    321        /**
    322         * Constant for the key for defining providers in the configuration file.
    323         */
    324        static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER
    325                + ".providers.provider";
    326
    327        /**
    328         * Constant for the tag attribute for providers.
    329         */
    330        static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]";
    331
    332        /**
    333         * Constant for the key for defining variable resolvers
    334         */
    335        static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER
    336                + ".lookups.lookup";
    337
    338        /**
    339         * Constant for the key for defining entity resolvers
    340         */
    341        static final String KEY_ENTITY_RESOLVER = SEC_HEADER + ".entity-resolver";
    342
    343        /**
    344         * Constant for the prefix attribute for lookups.
    345         */
    346        static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]";
    347
    348        /**
    349         * Constance for the FileSystem.
    350         */
    351        static final String FILE_SYSTEM = SEC_HEADER + ".fileSystem";
    352
    353        /**
    354         * Constant for the key of the result declaration. This key can point to a
    355         * bean declaration, which defines properties of the resulting combined
    356         * configuration.
    357         */
    358        static final String KEY_RESULT = SEC_HEADER + ".result";
    359
    360        /** Constant for the key of the combiner in the result declaration.*/
    361        static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
    362
    363        /** Constant for the XML file extension. */
    364        static final String EXT_XML = ".xml";
    365
    366        /** Constant for the provider for properties files. */
    367        private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
    368                XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
    369                EXT_XML);
    370
    371        /** Constant for the provider for XML files. */
    372        private static final ConfigurationProvider XML_PROVIDER = new XMLConfigurationProvider();
    373
    374        /** Constant for the provider for JNDI sources. */
    375        private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
    376                JNDIConfiguration.class);
    377
    378        /** Constant for the provider for system properties. */
    379        private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
    380                SystemConfiguration.class);
    381
    382        /** Constant for the provider for ini files. */
    383        private static final ConfigurationProvider INI_PROVIDER =
    384                new FileConfigurationProvider(HierarchicalINIConfiguration.class);
    385
    386        /** Constant for the provider for environment properties. */
    387        private static final ConfigurationProvider ENV_PROVIDER =
    388                new ConfigurationProvider(EnvironmentConfiguration.class);
    389
    390        /** Constant for the provider for plist files. */
    391        private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
    392                "org.apache.commons.configuration.plist.XMLPropertyListConfiguration",
    393                "org.apache.commons.configuration.plist.PropertyListConfiguration",
    394                EXT_XML);
    395
    396        /** Constant for the provider for configuration definition files.*/
    397        private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
    398
    399        /** An array with the names of the default tags. */
    400        private static final String[] DEFAULT_TAGS = {
    401                "properties", "xml", "hierarchicalXml", "jndi", "system", "plist",
    402                "configuration", "ini", "env"
    403        };
    404
    405        /** An array with the providers for the default tags. */
    406        private static final ConfigurationProvider[] DEFAULT_PROVIDERS = {
    407                PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
    408                SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER, INI_PROVIDER,
    409                ENV_PROVIDER
    410        };
    411
    412        /**
    413         * The serial version UID.
    414         */
    415        private static final long serialVersionUID = -3113777854714492123L;
    416
    417        /** Stores the configuration that is currently constructed.*/
    418        private CombinedConfiguration constructedConfiguration;
    419
    420        /** Stores a map with the registered configuration providers. */
    421        private Map<String, ConfigurationProvider> 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}. 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<String, ConfigurationProvider>();
    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} 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} 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} 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<SubnodeConfiguration> overrides = fetchTopLevelOverrideConfigs();
    600            overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
    601            initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
    602
    603            List<SubnodeConfiguration> 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()}. It checks whether the
    618         * {@code header} section of the configuration definition file
    619         * contains a {@code result} 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} 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} 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} 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<? extends HierarchicalConfiguration> containedConfigs,
    681                String keyListNodes) throws ConfigurationException
    682        {
    683            List<Object> listNodes = getList(keyListNodes);
    684            for (Object listNode : listNodes)
    685            {
    686                config.getNodeCombiner().addListNode((String) listNode);
    687            }
    688
    689            for (HierarchicalConfiguration conf : containedConfigs)
    690            {
    691                ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
    692                        conf);
    693                if (getLogger().isDebugEnabled())
    694                {
    695                    getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name "
    696                        + decl.getConfiguration().getString(ATTR_NAME));
    697                }
    698                AbstractConfiguration newConf = createConfigurationAt(decl);
    699                if (newConf != null)
    700                {
    701                    config.addConfiguration(newConf, decl.getConfiguration()
    702                            .getString(ATTR_NAME), decl.getAt());
    703                }
    704            }
    705        }
    706
    707        /**
    708         * Registers the default configuration providers supported by this class.
    709         * This method will be called during initialization. It registers
    710         * configuration providers for the tags that are supported by default.
    711         */
    712        protected void registerDefaultProviders()
    713        {
    714            for (int i = 0; i < DEFAULT_TAGS.length; i++)
    715            {
    716                addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
    717            }
    718        }
    719
    720        /**
    721         * Registers providers defined in the configuration.
    722         *
    723         * @throws ConfigurationException if an error occurs
    724         */
    725        protected void registerConfiguredProviders() throws ConfigurationException
    726        {
    727            List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS);
    728            for (HierarchicalConfiguration config : nodes)
    729            {
    730                XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
    731                String key = config.getString(KEY_PROVIDER_KEY);
    732                addConfigurationProvider(key, (ConfigurationProvider) BeanHelper
    733                        .createBean(decl));
    734            }
    735        }
    736
    737        /**
    738         * Registers StrLookups defined in the configuration.
    739         *
    740         * @throws ConfigurationException if an error occurs
    741         */
    742        protected void registerConfiguredLookups() throws ConfigurationException
    743        {
    744            List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS);
    745            for (HierarchicalConfiguration config : nodes)
    746            {
    747                XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
    748                String key = config.getString(KEY_LOOKUP_KEY);
    749                StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
    750                BeanHelper.setProperty(lookup, "configuration", this);
    751                ConfigurationInterpolator.registerGlobalLookup(key, lookup);
    752                this.getInterpolator().registerLookup(key, lookup);
    753            }
    754        }
    755
    756        protected void initFileSystem() throws ConfigurationException
    757        {
    758            if (getMaxIndex(FILE_SYSTEM) == 0)
    759            {
    760                HierarchicalConfiguration config = configurationAt(FILE_SYSTEM);
    761                XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
    762                setFileSystem((FileSystem) BeanHelper.createBean(decl));
    763            }
    764        }
    765
    766        /**
    767         * If a property file is configured add the properties to the System properties.
    768         * @throws ConfigurationException if an error occurs.
    769         */
    770        protected void initSystemProperties() throws ConfigurationException
    771        {
    772            String fileName = getString(KEY_SYSTEM_PROPS);
    773            if (fileName != null)
    774            {
    775                try
    776                {
    777                   SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName);
    778                }
    779                catch (Exception ex)
    780                {
    781                    throw new ConfigurationException("Error setting system properties from " + fileName, ex);
    782                }
    783
    784            }
    785        }
    786
    787        protected void configureEntityResolver() throws ConfigurationException
    788        {
    789            if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0)
    790            {
    791                XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true);
    792                EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class);
    793                BeanHelper.setProperty(resolver, "fileSystem", getFileSystem());
    794                BeanHelper.setProperty(resolver, "baseDir", getBasePath());
    795                BeanHelper.setProperty(resolver, "substitutor", getSubstitutor());
    796                setEntityResolver(resolver);
    797            }
    798        }
    799
    800        /**
    801         * Performs interpolation. This method will not only take this configuration
    802         * instance into account (which is the one that loaded the configuration
    803         * definition file), but also the so far constructed combined configuration.
    804         * So variables can be used that point to properties that are defined in
    805         * configuration sources loaded by this builder.
    806         *
    807         * @param value the value to be interpolated
    808         * @return the interpolated value
    809         */
    810        @Override
    811        protected Object interpolate(Object value)
    812        {
    813            Object result = super.interpolate(value);
    814            if (constructedConfiguration != null)
    815            {
    816                result = constructedConfiguration.interpolate(result);
    817            }
    818            return result;
    819        }
    820
    821        /**
    822         * Creates a configuration object from the specified configuration
    823         * declaration.
    824         *
    825         * @param decl the configuration declaration
    826         * @return the new configuration object
    827         * @throws ConfigurationException if an error occurs
    828         */
    829        private AbstractConfiguration createConfigurationAt(
    830                ConfigurationDeclaration decl) throws ConfigurationException
    831        {
    832            try
    833            {
    834                return (AbstractConfiguration) BeanHelper.createBean(decl);
    835            }
    836            catch (Exception ex)
    837            {
    838                // redirect to configuration exceptions
    839                throw new ConfigurationException(ex);
    840            }
    841        }
    842
    843        /**
    844         * Returns a list with {@code SubnodeConfiguration} objects for the
    845         * child nodes of the specified configuration node.
    846         *
    847         * @param node the start node
    848         * @return a list with subnode configurations for the node's children
    849         */
    850        private List<SubnodeConfiguration> fetchChildConfigs(ConfigurationNode node)
    851        {
    852            List<ConfigurationNode> children = node.getChildren();
    853            List<SubnodeConfiguration> result = new ArrayList<SubnodeConfiguration>(children.size());
    854            for (ConfigurationNode child : children)
    855            {
    856                result.add(createSubnodeConfiguration(child));
    857            }
    858            return result;
    859        }
    860
    861        /**
    862         * Returns a list with {@code SubnodeConfiguration} objects for the
    863         * child nodes of the node specified by the given key.
    864         *
    865         * @param key the key (must define exactly one node)
    866         * @return a list with subnode configurations for the node's children
    867         */
    868        private List<SubnodeConfiguration> fetchChildConfigs(String key)
    869        {
    870            List<ConfigurationNode> nodes = fetchNodeList(key);
    871            if (nodes.size() > 0)
    872            {
    873                return fetchChildConfigs(nodes.get(0));
    874            }
    875            else
    876            {
    877                return Collections.emptyList();
    878            }
    879        }
    880
    881        /**
    882         * Finds the override configurations that are defined as top level elements
    883         * in the configuration definition file. This method will fetch the child
    884         * elements of the root node and remove the nodes that represent other
    885         * configuration sections. The remaining nodes are treated as definitions
    886         * for override configurations.
    887         *
    888         * @return a list with subnode configurations for the top level override
    889         * configurations
    890         */
    891        private List<SubnodeConfiguration> fetchTopLevelOverrideConfigs()
    892        {
    893            List<SubnodeConfiguration> configs = fetchChildConfigs(getRootNode());
    894            for (Iterator<SubnodeConfiguration> it = configs.iterator(); it.hasNext();)
    895            {
    896                String nodeName = it.next().getRootNode().getName();
    897                for (int i = 0; i < CONFIG_SECTIONS.length; i++)
    898                {
    899                    if (CONFIG_SECTIONS[i].equals(nodeName))
    900                    {
    901                        it.remove();
    902                        break;
    903                    }
    904                }
    905            }
    906            return configs;
    907        }
    908
    909        /**
    910         * Registers the bean factory used by this class if necessary. This method
    911         * is called by the constructor to ensure that the required bean factory is
    912         * available.
    913         */
    914        private void registerBeanFactory()
    915        {
    916            synchronized (DefaultConfigurationBuilder.class)
    917            {
    918                if (!BeanHelper.registeredFactoryNames().contains(
    919                        CONFIG_BEAN_FACTORY_NAME))
    920                {
    921                    BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
    922                            new ConfigurationBeanFactory());
    923                }
    924            }
    925        }
    926
    927        /**
    928         * <p>
    929         * A base class for creating and initializing configuration sources.
    930         * </p>
    931         * <p>
    932         * Concrete sub classes of this base class are responsible for creating
    933         * specific {@code Configuration} objects for the tags in the
    934         * configuration definition file. The configuration factory will parse the
    935         * definition file and try to find a matching
    936         * {@code ConfigurationProvider} for each encountered tag. This
    937         * provider is then asked to create a corresponding
    938         * {@code Configuration} object. It is up to a concrete
    939         * implementation how this object is created and initialized.
    940         * </p>
    941         * <p>
    942         * Note that at the moment only configuration classes derived from
    943         * {@link AbstractConfiguration} are supported.
    944         * </p>
    945         */
    946        public static class ConfigurationProvider extends DefaultBeanFactory
    947        {
    948            /** Stores the class of the configuration to be created. */
    949            private Class<?> configurationClass;
    950
    951            /** Stores the name of the configuration class to be created.*/
    952            private String configurationClassName;
    953
    954            /**
    955             * Creates a new uninitialized instance of {@code ConfigurationProvider}.
    956             */
    957            public ConfigurationProvider()
    958            {
    959                this((Class<?>) null);
    960            }
    961
    962            /**
    963             * Creates a new instance of {@code ConfigurationProvider} and
    964             * sets the class of the configuration created by this provider.
    965             *
    966             * @param configClass the configuration class
    967             */
    968            public ConfigurationProvider(Class<?> configClass)
    969            {
    970                setConfigurationClass(configClass);
    971            }
    972
    973            /**
    974             * Creates a new instance of {@code ConfigurationProvider} and
    975             * sets the name of the class of the configuration created by this
    976             * provider.
    977             *
    978             * @param configClassName the name of the configuration class
    979             * @since 1.4
    980             */
    981            public ConfigurationProvider(String configClassName)
    982            {
    983                setConfigurationClassName(configClassName);
    984            }
    985
    986            /**
    987             * Returns the class of the configuration returned by this provider.
    988             *
    989             * @return the class of the provided configuration
    990             */
    991            public Class<?> getConfigurationClass()
    992            {
    993                return configurationClass;
    994            }
    995
    996            /**
    997             * Sets the class of the configuration returned by this provider.
    998             *
    999             * @param configurationClass the configuration class
    1000             */
    1001            public void setConfigurationClass(Class<?> configurationClass)
    1002            {
    1003                this.configurationClass = configurationClass;
    1004            }
    1005
    1006            /**
    1007             * Returns the name of the configuration class returned by this
    1008             * provider.
    1009             *
    1010             * @return the configuration class name
    1011             * @since 1.4
    1012             */
    1013            public String getConfigurationClassName()
    1014            {
    1015                return configurationClassName;
    1016            }
    1017
    1018            /**
    1019             * Sets the name of the configuration class returned by this provider.
    1020             *
    1021             * @param configurationClassName the name of the configuration class
    1022             * @since 1.4
    1023             */
    1024            public void setConfigurationClassName(String configurationClassName)
    1025            {
    1026                this.configurationClassName = configurationClassName;
    1027            }
    1028
    1029            /**
    1030             * Returns the configuration. This method is called to fetch the
    1031             * configuration from the provider. This implementation will call the
    1032             * inherited {@link
    1033             * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
    1034             * createBean()} method to create a new instance of the
    1035             * configuration class.
    1036             *
    1037             * @param decl the bean declaration with initialization parameters for
    1038             * the configuration
    1039             * @return the new configuration object
    1040             * @throws Exception if an error occurs
    1041             */
    1042            public AbstractConfiguration getConfiguration(
    1043                    ConfigurationDeclaration decl) throws Exception
    1044            {
    1045                return (AbstractConfiguration) createBean(fetchConfigurationClass(),
    1046                        decl, null);
    1047            }
    1048
    1049            /**
    1050             * Returns an uninitialized configuration of the represented type. This
    1051             * method will be called for optional configurations when the
    1052             * {@code getConfiguration()} method caused an error and the
    1053             * {@code forceCreate} attribute is set. A concrete sub class can
    1054             * here try to create an uninitialized, empty configuration, which may
    1055             * be possible if the error was created during initialization. This base
    1056             * implementation just returns <b>null</b>.
    1057             *
    1058             * @param decl the bean declaration with initialization parameters for
    1059             * the configuration
    1060             * @return the new configuration object
    1061             * @throws Exception if an error occurs
    1062             * @since 1.4
    1063             */
    1064            public AbstractConfiguration getEmptyConfiguration(
    1065                    ConfigurationDeclaration decl) throws Exception
    1066            {
    1067                return null;
    1068            }
    1069
    1070            /**
    1071             * Returns the configuration class supported by this provider. If a
    1072             * class object was set, it is returned. Otherwise the method tries to
    1073             * resolve the class name.
    1074             *
    1075             * @return the class of the configuration to be created
    1076             * @since 1.4
    1077             */
    1078            protected synchronized Class<?> fetchConfigurationClass() throws Exception
    1079            {
    1080                if (getConfigurationClass() == null)
    1081                {
    1082                    setConfigurationClass(loadClass(getConfigurationClassName()));
    1083                }
    1084                return getConfigurationClass();
    1085            }
    1086
    1087            /**
    1088             * Loads the class with the specified name dynamically. If the class's
    1089             * name is <b>null</b>, <b>null</b> will also be returned.
    1090             *
    1091             * @param className the name of the class to be loaded
    1092             * @return the class object
    1093             * @throws ClassNotFoundException if class loading fails
    1094             * @since 1.4
    1095             */
    1096            protected Class<?> loadClass(String className)
    1097                    throws ClassNotFoundException
    1098            {
    1099                return (className != null) ? Class.forName(className, true,
    1100                        getClass().getClassLoader()) : null;
    1101            }
    1102        }
    1103
    1104        /**
    1105         * <p>
    1106         * A specialized {@code BeanDeclaration} implementation that
    1107         * represents the declaration of a configuration source.
    1108         * </p>
    1109         * <p>
    1110         * Instances of this class are able to extract all information about a
    1111         * configuration source from the configuration definition file. The
    1112         * declaration of a configuration source is very similar to a bean
    1113         * declaration processed by {@code XMLBeanDeclaration}. There are
    1114         * very few differences, e.g. some reserved attributes like
    1115         * {@code optional} and {@code at} and the fact that a bean
    1116         * factory is never needed.
    1117         * </p>
    1118         */
    1119        public static class ConfigurationDeclaration extends XMLBeanDeclaration
    1120        {
    1121            /** Stores a reference to the associated configuration builder. */
    1122            private DefaultConfigurationBuilder configurationBuilder;
    1123
    1124            /**
    1125             * Creates a new instance of {@code ConfigurationDeclaration} and
    1126             * initializes it.
    1127             *
    1128             * @param builder the associated configuration builder
    1129             * @param config the configuration this declaration is based onto
    1130             */
    1131            public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
    1132                    HierarchicalConfiguration config)
    1133            {
    1134                super(config);
    1135                configurationBuilder = builder;
    1136            }
    1137
    1138            /**
    1139             * Returns the associated configuration builder.
    1140             *
    1141             * @return the configuration builder
    1142             */
    1143            public DefaultConfigurationBuilder getConfigurationBuilder()
    1144            {
    1145                return configurationBuilder;
    1146            }
    1147
    1148            /**
    1149             * Returns the value of the {@code at} attribute.
    1150             *
    1151             * @return the value of the {@code at} attribute (can be <b>null</b>)
    1152             */
    1153            public String getAt()
    1154            {
    1155                String result = this.getConfiguration().getString(ATTR_AT_RES);
    1156                return (result == null) ? this.getConfiguration().getString(ATTR_AT)
    1157                        : result;
    1158            }
    1159
    1160            /**
    1161             * Returns a flag whether this is an optional configuration.
    1162             *
    1163             * @return a flag if this declaration points to an optional
    1164             * configuration
    1165             */
    1166            public boolean isOptional()
    1167            {
    1168                Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
    1169                        null);
    1170                if (value == null)
    1171                {
    1172                    value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
    1173                            Boolean.FALSE);
    1174                }
    1175                return value.booleanValue();
    1176            }
    1177
    1178            /**
    1179             * Returns a flag whether this configuration should always be created
    1180             * and added to the resulting combined configuration. This flag is
    1181             * evaluated only for optional configurations whose normal creation has
    1182             * caused an error. If for such a configuration the
    1183             * {@code forceCreate} attribute is set and the corresponding
    1184             * configuration provider supports this mode, an empty configuration
    1185             * will be created and added to the resulting combined configuration.
    1186             *
    1187             * @return the value of the {@code forceCreate} attribute
    1188             * @since 1.4
    1189             */
    1190            public boolean isForceCreate()
    1191            {
    1192                return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
    1193            }
    1194
    1195            /**
    1196             * Returns the name of the bean factory. For configuration source
    1197             * declarations always a reserved factory is used. This factory's name
    1198             * is returned by this implementation.
    1199             *
    1200             * @return the name of the bean factory
    1201             */
    1202            @Override
    1203            public String getBeanFactoryName()
    1204            {
    1205                return CONFIG_BEAN_FACTORY_NAME;
    1206            }
    1207
    1208            /**
    1209             * Returns the bean's class name. This implementation will always return
    1210             * <b>null</b>.
    1211             *
    1212             * @return the name of the bean's class
    1213             */
    1214            @Override
    1215            public String getBeanClassName()
    1216            {
    1217                return null;
    1218            }
    1219
    1220            /**
    1221             * Checks whether the given node is reserved. This method will take
    1222             * further reserved attributes into account
    1223             *
    1224             * @param nd the node
    1225             * @return a flag whether this node is reserved
    1226             */
    1227            @Override
    1228            protected boolean isReservedNode(ConfigurationNode nd)
    1229            {
    1230                if (super.isReservedNode(nd))
    1231                {
    1232                    return true;
    1233                }
    1234
    1235                return nd.isAttribute()
    1236                        && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
    1237                                .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
    1238                                .equals(nd.getName()) && nd.getParentNode()
    1239                                .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
    1240            }
    1241
    1242            /**
    1243             * Performs interpolation. This implementation will delegate
    1244             * interpolation to the configuration builder, which takes care that the
    1245             * currently constructed configuration is taken into account, too.
    1246             *
    1247             * @param value the value to be interpolated
    1248             * @return the interpolated value
    1249             */
    1250            @Override
    1251            protected Object interpolate(Object value)
    1252            {
    1253                return getConfigurationBuilder().interpolate(value);
    1254            }
    1255        }
    1256
    1257        /**
    1258         * A specialized {@code BeanFactory} implementation that handles
    1259         * configuration declarations. This class will retrieve the correct
    1260         * configuration provider and delegate the task of creating the
    1261         * configuration to this object.
    1262         */
    1263        static class ConfigurationBeanFactory implements BeanFactory
    1264        {
    1265            /** The logger. */
    1266            private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class);
    1267
    1268            /**
    1269             * Creates an instance of a bean class. This implementation expects that
    1270             * the passed in bean declaration is a declaration for a configuration.
    1271             * It will determine the responsible configuration provider and delegate
    1272             * the call to this instance. If creation of the configuration fails
    1273             * and the {@code optional} attribute is set, the exception will
    1274             * be ignored. If the {@code forceCreate} attribute is set, too,
    1275             * the provider is asked to create an empty configuration. A return
    1276             * value of <b>null</b> means that no configuration could be created.
    1277             *
    1278             * @param beanClass the bean class (will be ignored)
    1279             * @param data the declaration
    1280             * @param param an additional parameter (will be ignored)
    1281             * @return the newly created configuration
    1282             * @throws Exception if an error occurs
    1283             */
    1284            public Object createBean(Class<?> beanClass, BeanDeclaration data,
    1285                    Object param) throws Exception
    1286            {
    1287                ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
    1288                String tagName = decl.getNode().getName();
    1289                ConfigurationProvider provider = decl.getConfigurationBuilder()
    1290                        .providerForTag(tagName);
    1291                if (provider == null)
    1292                {
    1293                    throw new ConfigurationRuntimeException(
    1294                            "No ConfigurationProvider registered for tag "
    1295                                    + tagName);
    1296                }
    1297
    1298                try
    1299                {
    1300                    return provider.getConfiguration(decl);
    1301                }
    1302                catch (Exception ex)
    1303                {
    1304                    // If this is an optional configuration, ignore the exception
    1305                    if (!decl.isOptional())
    1306                    {
    1307                        throw ex;
    1308                    }
    1309                    else
    1310                    {
    1311                        if (logger.isDebugEnabled())
    1312                        {
    1313                            logger.debug("Load failed for optional configuration " + tagName + ": "
    1314                                + ex.getMessage());
    1315                        }
    1316                        // Notify registered error listeners
    1317                        decl.getConfigurationBuilder().fireError(
    1318                                EVENT_ERR_LOAD_OPTIONAL,
    1319                                decl.getConfiguration().getString(ATTR_NAME), null,
    1320                                ex);
    1321
    1322                        if (decl.isForceCreate())
    1323                        {
    1324                            try
    1325                            {
    1326                                return provider.getEmptyConfiguration(decl);
    1327                            }
    1328                            catch (Exception ex2)
    1329                            {
    1330                                // Ignore exception, return null in this case
    1331                                ;
    1332                            }
    1333                        }
    1334                        return null;
    1335                    }
    1336                }
    1337            }
    1338
    1339            /**
    1340             * Returns the default class for this bean factory.
    1341             *
    1342             * @return the default class
    1343             */
    1344            public Class<?> getDefaultBeanClass()
    1345            {
    1346                // Here some valid class must be returned, otherwise BeanHelper
    1347                // will complain that the bean's class cannot be determined
    1348                return Configuration.class;
    1349            }
    1350        }
    1351
    1352        /**
    1353         * A specialized provider implementation that deals with file based
    1354         * configurations. Ensures that the base path is correctly set and that the
    1355         * load() method gets called.
    1356         */
    1357        public static class FileConfigurationProvider extends ConfigurationProvider
    1358        {
    1359            /**
    1360             * Creates a new instance of {@code FileConfigurationProvider}.
    1361             */
    1362            public FileConfigurationProvider()
    1363            {
    1364                super();
    1365            }
    1366
    1367            /**
    1368             * Creates a new instance of {@code FileConfigurationProvider}
    1369             * and sets the configuration class.
    1370             *
    1371             * @param configClass the class for the configurations to be created
    1372             */
    1373            public FileConfigurationProvider(Class<?> configClass)
    1374            {
    1375                super(configClass);
    1376            }
    1377
    1378            /**
    1379             * Creates a new instance of {@code FileConfigurationProvider}
    1380             * and sets the configuration class name.
    1381             *
    1382             * @param configClassName the name of the configuration to be created
    1383             * @since 1.4
    1384             */
    1385            public FileConfigurationProvider(String configClassName)
    1386            {
    1387                super(configClassName);
    1388            }
    1389
    1390            /**
    1391             * Creates the configuration. After that {@code load()} will be
    1392             * called. If this configuration is marked as optional, exceptions will
    1393             * be ignored.
    1394             *
    1395             * @param decl the declaration
    1396             * @return the new configuration
    1397             * @throws Exception if an error occurs
    1398             */
    1399            @Override
    1400            public AbstractConfiguration getConfiguration(
    1401                    ConfigurationDeclaration decl) throws Exception
    1402            {
    1403                AbstractConfiguration result = getEmptyConfiguration(decl);
    1404                if (result instanceof FileSystemBased)
    1405                {
    1406                    DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
    1407                    if (builder.getFileSystem() != null)
    1408                    {
    1409                        ((FileSystemBased) result).setFileSystem(builder.getFileSystem());
    1410                    }
    1411                }
    1412                ((FileConfiguration) result).load();
    1413                return result;
    1414            }
    1415
    1416            /**
    1417             * Returns an uninitialized file configuration. This method will be
    1418             * called for optional configurations when the
    1419             * {@code getConfiguration()} method caused an error and the
    1420             * {@code forceCreate} attribute is set. It will create the
    1421             * configuration of the represented type, but the {@code load()}
    1422             * method won't be called. This way non-existing configuration files can
    1423             * be handled gracefully: If loading a the file fails, an empty
    1424             * configuration will be created that is already configured with the
    1425             * correct file name.
    1426             *
    1427             * @param decl the bean declaration with initialization parameters for
    1428             * the configuration
    1429             * @return the new configuration object
    1430             * @throws Exception if an error occurs
    1431             * @since 1.4
    1432             */
    1433            @Override
    1434            public AbstractConfiguration getEmptyConfiguration(
    1435                    ConfigurationDeclaration decl) throws Exception
    1436            {
    1437                AbstractConfiguration config = super.getConfiguration(decl);
    1438
    1439                /**
    1440                 * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations
    1441                 * they construct buy may not be an XMLConfiguration.
    1442                 */
    1443                if (config instanceof EntityResolverSupport)
    1444                {
    1445                    DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
    1446                    EntityResolver resolver = builder.getEntityResolver();
    1447                    ((EntityResolverSupport) config).setEntityResolver(resolver);
    1448                }
    1449
    1450                return config;
    1451            }
    1452
    1453            /**
    1454             * Initializes the bean instance. Ensures that the file configuration's
    1455             * base path will be initialized with the base path of the factory so
    1456             * that relative path names can be correctly resolved.
    1457             *
    1458             * @param bean the bean to be initialized
    1459             * @param data the declaration
    1460             * @throws Exception if an error occurs
    1461             */
    1462            @Override
    1463            protected void initBeanInstance(Object bean, BeanDeclaration data)
    1464                    throws Exception
    1465            {
    1466                FileConfiguration config = (FileConfiguration) bean;
    1467                config.setBasePath(((ConfigurationDeclaration) data)
    1468                        .getConfigurationBuilder().getConfigurationBasePath());
    1469                super.initBeanInstance(bean, data);
    1470            }
    1471        }
    1472
    1473        /**
    1474         * A specialized configuration provider for XML configurations. This
    1475         * implementation acts like a {@code FileConfigurationProvider}, but
    1476         * it will copy all entity IDs that have been registered for the
    1477         * configuration builder to the new XML configuration before it is loaded.
    1478         *
    1479         * @since 1.6
    1480         */
    1481        public static class XMLConfigurationProvider extends FileConfigurationProvider
    1482        {
    1483            /**
    1484             * Creates a new instance of {@code XMLConfigurationProvider}.
    1485             */
    1486            public XMLConfigurationProvider()
    1487            {
    1488                super(XMLConfiguration.class);
    1489            }
    1490
    1491            /**
    1492             * Returns a new empty configuration instance. This implementation
    1493             * performs some additional initialization specific to XML
    1494             * configurations.
    1495             *
    1496             * @param decl the configuration declaration
    1497             * @return the new configuration
    1498             * @throws Exception if an error occurs
    1499             */
    1500            @Override
    1501            public AbstractConfiguration getEmptyConfiguration(
    1502                    ConfigurationDeclaration decl) throws Exception
    1503            {
    1504                XMLConfiguration config = (XMLConfiguration) super
    1505                        .getEmptyConfiguration(decl);
    1506
    1507                DefaultConfigurationBuilder builder = decl
    1508                        .getConfigurationBuilder();
    1509                EntityResolver resolver = builder.getEntityResolver();
    1510                if (resolver instanceof EntityRegistry)
    1511                {
    1512                    // copy the registered entities
    1513                    config.getRegisteredEntities().putAll(
    1514                        builder.getRegisteredEntities());
    1515                }
    1516                else
    1517                {
    1518                    config.setEntityResolver(resolver);
    1519                }
    1520                return config;
    1521            }
    1522        }
    1523
    1524        /**
    1525         * A specialized configuration provider for file based configurations that
    1526         * can handle configuration sources whose concrete type depends on the
    1527         * extension of the file to be loaded. One example is the
    1528         * {@code properties} tag: if the file ends with ".xml" a
    1529         * XMLPropertiesConfiguration object must be created, otherwise a
    1530         * PropertiesConfiguration object.
    1531         */
    1532        static class FileExtensionConfigurationProvider extends
    1533                FileConfigurationProvider
    1534        {
    1535            /**
    1536             * Stores the class to be created when the file extension matches.
    1537             */
    1538            private Class<?> matchingClass;
    1539
    1540            /**
    1541             * Stores the name of the class to be created when the file extension
    1542             * matches.
    1543             */
    1544            private String matchingClassName;
    1545
    1546            /**
    1547             * Stores the class to be created when the file extension does not
    1548             * match.
    1549             */
    1550            private Class<?> defaultClass;
    1551
    1552            /**
    1553             * Stores the name of the class to be created when the file extension
    1554             * does not match.
    1555             */
    1556            private String defaultClassName;
    1557
    1558            /** Stores the file extension to be checked against. */
    1559            private String fileExtension;
    1560
    1561            /**
    1562             * Creates a new instance of
    1563             * {@code FileExtensionConfigurationProvider} and initializes it.
    1564             *
    1565             * @param matchingClass the class to be created when the file extension
    1566             * matches
    1567             * @param defaultClass the class to be created when the file extension
    1568             * does not match
    1569             * @param extension the file extension to be checked against
    1570             */
    1571            public FileExtensionConfigurationProvider(Class<?> matchingClass,
    1572                    Class<?> defaultClass, String extension)
    1573            {
    1574                this.matchingClass = matchingClass;
    1575                this.defaultClass = defaultClass;
    1576                fileExtension = extension;
    1577            }
    1578
    1579            /**
    1580             * Creates a new instance of
    1581             * {@code FileExtensionConfigurationProvider} and initializes it
    1582             * with the names of the classes to be created.
    1583             *
    1584             * @param matchingClassName the name of the class to be created when the
    1585             * file extension matches
    1586             * @param defaultClassName the name of the class to be created when the
    1587             * file extension does not match
    1588             * @param extension the file extension to be checked against
    1589             * @since 1.4
    1590             */
    1591            public FileExtensionConfigurationProvider(String matchingClassName,
    1592                    String defaultClassName, String extension)
    1593            {
    1594                this.matchingClassName = matchingClassName;
    1595                this.defaultClassName = defaultClassName;
    1596                fileExtension = extension;
    1597            }
    1598
    1599            /**
    1600             * Returns the matching class object, no matter whether it was defined
    1601             * as a class or as a class name.
    1602             *
    1603             * @return the matching class object
    1604             * @throws Exception if an error occurs
    1605             * @since 1.4
    1606             */
    1607            protected synchronized Class<?> fetchMatchingClass() throws Exception
    1608            {
    1609                if (matchingClass == null)
    1610                {
    1611                    matchingClass = loadClass(matchingClassName);
    1612                }
    1613                return matchingClass;
    1614            }
    1615
    1616            /**
    1617             * Returns the default class object, no matter whether it was defined as
    1618             * a class or as a class name.
    1619             *
    1620             * @return the default class object
    1621             * @throws Exception if an error occurs
    1622             * @since 1.4
    1623             */
    1624            protected synchronized Class<?> fetchDefaultClass() throws Exception
    1625            {
    1626                if (defaultClass == null)
    1627                {
    1628                    defaultClass = loadClass(defaultClassName);
    1629                }
    1630                return defaultClass;
    1631            }
    1632
    1633            /**
    1634             * Creates the configuration object. The class is determined by the file
    1635             * name's extension.
    1636             *
    1637             * @param beanClass the class
    1638             * @param data the bean declaration
    1639             * @return the new bean
    1640             * @throws Exception if an error occurs
    1641             */
    1642            @Override
    1643            protected Object createBeanInstance(Class<?> beanClass,
    1644                    BeanDeclaration data) throws Exception
    1645            {
    1646                String fileName = ((ConfigurationDeclaration) data)
    1647                        .getConfiguration().getString(ATTR_FILENAME);
    1648                if (fileName != null
    1649                        && fileName.toLowerCase().trim().endsWith(fileExtension))
    1650                {
    1651                    return super.createBeanInstance(fetchMatchingClass(), data);
    1652                }
    1653                else
    1654                {
    1655                    return super.createBeanInstance(fetchDefaultClass(), data);
    1656                }
    1657            }
    1658        }
    1659
    1660        /**
    1661         * A specialized configuration provider class that allows to include other
    1662         * configuration definition files.
    1663         */
    1664        static class ConfigurationBuilderProvider extends ConfigurationProvider
    1665        {
    1666            /**
    1667             * Creates a new instance of {@code ConfigurationBuilderProvider}.
    1668             */
    1669            public ConfigurationBuilderProvider()
    1670            {
    1671                super(DefaultConfigurationBuilder.class);
    1672            }
    1673
    1674            /**
    1675             * Creates the configuration. First creates a configuration builder
    1676             * object. Then returns the configuration created by this builder.
    1677             *
    1678             * @param decl the configuration declaration
    1679             * @return the configuration
    1680             * @exception Exception if an error occurs
    1681             */
    1682            @Override
    1683            public AbstractConfiguration getConfiguration(
    1684                    ConfigurationDeclaration decl) throws Exception
    1685            {
    1686                DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
    1687                        .getConfiguration(decl);
    1688                return builder.getConfiguration(true);
    1689            }
    1690
    1691            /**
    1692             * Returns an empty configuration in case of an optional configuration
    1693             * could not be created. This implementation returns an empty combined
    1694             * configuration.
    1695             *
    1696             * @param decl the configuration declaration
    1697             * @return the configuration
    1698             * @exception Exception if an error occurs
    1699             * @since 1.4
    1700             */
    1701            @Override
    1702            public AbstractConfiguration getEmptyConfiguration(
    1703                    ConfigurationDeclaration decl) throws Exception
    1704            {
    1705                return new CombinedConfiguration();
    1706            }
    1707
    1708            /**
    1709             * {@inheritDoc} This implementation ensures that the configuration
    1710             * builder created by this provider inherits the properties from the
    1711             * current configuration builder.
    1712             */
    1713            @Override
    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 (ConfigurationListener l : parent.getConfigurationListeners())
    1747                {
    1748                    child.addConfigurationListener(l);
    1749                }
    1750                child.clearErrorListeners();
    1751                for (ConfigurationErrorListener l : parent.getErrorListeners())
    1752                {
    1753                    child.addErrorListener(l);
    1754                }
    1755            }
    1756        }
    1757    }