    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
    018    package org.apache.commons.configuration;
    019
    020    import java.io.File;
    021    import java.io.IOException;
    022    import java.io.InputStream;
    023    import java.net.URL;
    024    import java.util.Collection;
    025    import java.util.LinkedList;
    026    import java.util.Map;
    027
    028    import org.apache.commons.configuration.plist.PropertyListConfiguration;
    029    import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;
    030    import org.apache.commons.digester.AbstractObjectCreationFactory;
    031    import org.apache.commons.digester.CallMethodRule;
    032    import org.apache.commons.digester.Digester;
    033    import org.apache.commons.digester.ObjectCreationFactory;
    034    import org.apache.commons.digester.Substitutor;
    035    import org.apache.commons.digester.substitution.MultiVariableExpander;
    036    import org.apache.commons.digester.substitution.VariableSubstitutor;
    037    import org.apache.commons.digester.xmlrules.DigesterLoader;
    038    import org.apache.commons.lang.StringUtils;
    039    import org.apache.commons.logging.Log;
    040    import org.apache.commons.logging.LogFactory;
    041    import org.xml.sax.Attributes;
    042    import org.xml.sax.SAXException;
    043
    044    /**
    045     * <p>
    046     * Factory class to create a CompositeConfiguration from a .xml file using
    047     * Digester.  By default it can handle the Configurations from commons-
    048     * configuration.  If you need to add your own, then you can pass in your own
    049     * digester rules to use.  It is also namespace aware, by providing a
    050     * digesterRuleNamespaceURI.
    051     * </p>
    052     * <p>
    053     * <em>Note:</em> Almost all of the features provided by this class and many
    054     * more are also available for the {@link DefaultConfigurationBuilder}
    055     * class. {@code DefaultConfigurationBuilder} also has a more robust
    056     * merge algorithm for constructing combined configurations. So it is
    057     * recommended to use this class instead of {@code ConfigurationFactory}.
    058     * </p>
    059     *
    060     * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    061     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    062     * @version $Id: ConfigurationFactory.java 1209685 2011-12-02 20:47:44Z oheger $
    063     * @deprecated Use {@link DefaultConfigurationBuilder} instead; this class
    064     * provides the same features as ConfigurationFactory plus some more; it can
    065     * also process the same configuration definition files.
    066     */
    067    @Deprecated
    068    public class ConfigurationFactory
    069    {
    070        /** Constant for the root element in the info file.*/
    071        private static final String SEC_ROOT = "configuration/";
    072
    073        /** Constant for the override section.*/
    074        private static final String SEC_OVERRIDE = SEC_ROOT + "override/";
    075
    076        /** Constant for the additional section.*/
    077        private static final String SEC_ADDITIONAL = SEC_ROOT + "additional/";
    078
    079        /** Constant for the optional attribute.*/
    080        private static final String ATTR_OPTIONAL = "optional";
    081
    082        /** Constant for the fileName attribute.*/
    083        private static final String ATTR_FILENAME = "fileName";
    084
    085        /** Constant for the load method.*/
    086        private static final String METH_LOAD = "load";
    087
    088        /** Constant for the default base path (points to actual directory).*/
    089        private static final String DEF_BASE_PATH = ".";
    090
    091        /** static logger */
    092        private static Log log = LogFactory.getLog(ConfigurationFactory.class);
    093
    094        /** The XML file with the details about the configuration to load */
    095        private String configurationFileName;
    096
    097        /** The URL to the XML file with the details about the configuration to load. */
    098        private URL configurationURL;
    099
    100        /**
    101         * The implicit base path for included files. This path is determined by
    102         * the configuration to load and used unless no other base path was
    103         * explicitly specified.
    104         */
    105        private String implicitBasePath;
    106
    107        /** The basePath to prefix file paths for file based property files. */
    108        private String basePath;
    109
    110        /** URL for xml digester rules file */
    111        private URL digesterRules;
    112
    113        /** The digester namespace to parse */
    114        private String digesterRuleNamespaceURI;
    115
    116        /**
    117         * Constructor
    118         */
    119        public ConfigurationFactory()
    120        {
    121            setBasePath(DEF_BASE_PATH);
    122        }
    123        /**
    124         * Constructor with ConfigurationFile Name passed
    125         *
    126         * @param configurationFileName The path to the configuration file
    127         */
    128        public ConfigurationFactory(String configurationFileName)
    129        {
    130            setConfigurationFileName(configurationFileName);
    131        }
    132
    133        /**
    134         * Return the configuration provided by this factory. It loads the
    135         * configuration file which is a XML description of the actual
    136         * configurations to load. It can contain various different types of
    137         * configuration, e.g. Properties, XML and JNDI.
    138         *
    139         * @return A Configuration object
    140         * @throws ConfigurationException A generic exception that we had trouble during the
    141         * loading of the configuration data.
    142         */
    143        public Configuration getConfiguration() throws ConfigurationException
    144        {
    145            Digester digester;
    146            InputStream input = null;
    147            ConfigurationBuilder builder = new ConfigurationBuilder();
    148            URL url = getConfigurationURL();
    149            try
    150            {
    151                if (url == null)
    152                {
    153                    url = ConfigurationUtils.locate(implicitBasePath, getConfigurationFileName());
    154                }
    155                input = url.openStream();
    156            }
    157            catch (Exception e)
    158            {
    159                log.error("Exception caught opening stream to URL", e);
    160                throw new ConfigurationException("Exception caught opening stream to URL", e);
    161            }
    162
    163            if (getDigesterRules() == null)
    164            {
    165                digester = new Digester();
    166                configureNamespace(digester);
    167                initDefaultDigesterRules(digester);
    168            }
    169            else
    170            {
    171                digester = DigesterLoader.createDigester(getDigesterRules());
    172                // This might already be too late. As far as I can see, the namespace
    173                // awareness must be configured before the digester rules are loaded.
    174                configureNamespace(digester);
    175            }
    176
    177            // Configure digester to always enable the context class loader
    178            digester.setUseContextClassLoader(true);
    179            // Add a substitutor to resolve system properties
    180            enableDigesterSubstitutor(digester);
    181            // Put the composite builder object below all of the other objects.
    182            digester.push(builder);
    183            // Parse the input stream to configure our mappings
    184            try
    185            {
    186                digester.parse(input);
    187                input.close();
    188            }
    189            catch (SAXException saxe)
    190            {
    191                log.error("SAX Exception caught", saxe);
    192                throw new ConfigurationException("SAX Exception caught", saxe);
    193            }
    194            catch (IOException ioe)
    195            {
    196                log.error("IO Exception caught", ioe);
    197                throw new ConfigurationException("IO Exception caught", ioe);
    198            }
    199            return builder.getConfiguration();
    200        }
    201
    202        /**
    203         * Returns the configurationFile.
    204         *
    205         * @return The name of the configuration file. Can be null.
    206         */
    207        public String getConfigurationFileName()
    208        {
    209            return configurationFileName;
    210        }
    211
    212        /**
    213         * Sets the configurationFile.
    214         *
    215         * @param configurationFileName  The name of the configurationFile to use.
    216         */
    217        public void setConfigurationFileName(String configurationFileName)
    218        {
    219            File file = new File(configurationFileName).getAbsoluteFile();
    220            this.configurationFileName = file.getName();
    221            implicitBasePath = file.getParent();
    222        }
    223
    224        /**
    225         * Returns the URL of the configuration file to be loaded.
    226         *
    227         * @return the URL of the configuration to load
    228         */
    229        public URL getConfigurationURL()
    230        {
    231            return configurationURL;
    232        }
    233
    234        /**
    235         * Sets the URL of the configuration to load. This configuration can be
    236         * either specified by a file name or by a URL.
    237         *
    238         * @param url the URL of the configuration to load
    239         */
    240        public void setConfigurationURL(URL url)
    241        {
    242            configurationURL = url;
    243            implicitBasePath = url.toString();
    244        }
    245
    246        /**
    247         * Returns the digesterRules.
    248         *
    249         * @return URL
    250         */
    251        public URL getDigesterRules()
    252        {
    253            return digesterRules;
    254        }
    255
    256        /**
    257         * Sets the digesterRules.
    258         *
    259         * @param digesterRules The digesterRules to set
    260         */
    261        public void setDigesterRules(URL digesterRules)
    262        {
    263            this.digesterRules = digesterRules;
    264        }
    265
    266        /**
    267         * Adds a substitutor to interpolate system properties
    268         *
    269         * @param digester The digester to which we add the substitutor
    270         */
    271        protected void enableDigesterSubstitutor(Digester digester)
    272        {
    273            // This is ugly, but it is safe because the Properties object returned
    274            // by System.getProperties() (which is actually a Map<Object, Object>)
    275            // contains only String keys.
    276            @SuppressWarnings("unchecked")
    277            Map<String, Object> systemProperties =
    278                    (Map<String, Object>) (Object) System.getProperties();
    279            MultiVariableExpander expander = new MultiVariableExpander();
    280            expander.addSource("$", systemProperties);
    281
    282            // allow expansion in both xml attributes and element text
    283            Substitutor substitutor = new VariableSubstitutor(expander);
    284            digester.setSubstitutor(substitutor);
    285        }
    286
    287        /**
    288         * Initializes the parsing rules for the default digester
    289         *
    290         * This allows the Configuration Factory to understand the default types:
    291         * Properties, XML and JNDI. Two special sections are introduced:
    292         * <code>&lt;override&gt;</code> and <code>&lt;additional&gt;</code>.
    293         *
    294         * @param digester The digester to configure
    295         */
    296        protected void initDefaultDigesterRules(Digester digester)
    297        {
    298            initDigesterSectionRules(digester, SEC_ROOT, false);
    299            initDigesterSectionRules(digester, SEC_OVERRIDE, false);
    300            initDigesterSectionRules(digester, SEC_ADDITIONAL, true);
    301        }
    302
    303        /**
    304         * Sets up digester rules for a specified section of the configuration
    305         * info file.
    306         *
    307         * @param digester the current digester instance
    308         * @param matchString specifies the section
    309         * @param additional a flag if rules for the additional section are to be
    310         * added
    311         */
    312        protected void initDigesterSectionRules(Digester digester, String matchString, boolean additional)
    313        {
    314            setupDigesterInstance(
    315                digester,
    316                matchString + "properties",
    317                new PropertiesConfigurationFactory(),
    318                METH_LOAD,
    319                additional);
    320
    321            setupDigesterInstance(
    322                digester,
    323                matchString + "plist",
    324                new PropertyListConfigurationFactory(),
    325                METH_LOAD,
    326                additional);
    327
    328            setupDigesterInstance(
    329                digester,
    330                matchString + "xml",
    331                new FileConfigurationFactory(XMLConfiguration.class),
    332                METH_LOAD,
    333                additional);
    334
    335            setupDigesterInstance(
    336                digester,
    337                matchString + "hierarchicalXml",
    338                new FileConfigurationFactory(XMLConfiguration.class),
    339                METH_LOAD,
    340                additional);
    341
    342            setupDigesterInstance(
    343                digester,
    344                matchString + "jndi",
    345                new JNDIConfigurationFactory(),
    346                null,
    347                additional);
    348
    349            setupDigesterInstance(
    350                digester,
    351                matchString + "system",
    352                new SystemConfigurationFactory(),
    353                null,
    354                additional);
    355        }
    356
    357        /**
    358         * Sets up digester rules for a configuration to be loaded.
    359         *
    360         * @param digester the current digester
    361         * @param matchString the pattern to match with this rule
    362         * @param factory an ObjectCreationFactory instance to use for creating new
    363         * objects
    364         * @param method the name of a method to be called or <b>null</b> for none
    365         * @param additional a flag if rules for the additional section are to be
    366         * added
    367         */
    368        protected void setupDigesterInstance(
    369                Digester digester,
    370                String matchString,
    371                ObjectCreationFactory factory,
    372                String method,
    373                boolean additional)
    374        {
    375            if (additional)
    376            {
    377                setupUnionRules(digester, matchString);
    378            }
    379
    380            digester.addFactoryCreate(matchString, factory);
    381            digester.addSetProperties(matchString);
    382
    383            if (method != null)
    384            {
    385                digester.addRule(matchString, new CallOptionalMethodRule(method));
    386            }
    387
    388            digester.addSetNext(matchString, "addConfiguration", Configuration.class.getName());
    389        }
    390
    391        /**
    392         * Sets up rules for configurations in the additional section.
    393         *
    394         * @param digester the current digester
    395         * @param matchString the pattern to match with this rule
    396         */
    397        protected void setupUnionRules(Digester digester, String matchString)
    398        {
    399            digester.addObjectCreate(matchString,
    400            AdditionalConfigurationData.class);
    401            digester.addSetProperties(matchString);
    402            digester.addSetNext(matchString, "addAdditionalConfig",
    403            AdditionalConfigurationData.class.getName());
    404        }
    405
    406        /**
    407         * Returns the digesterRuleNamespaceURI.
    408         *
    409         * @return A String with the digesterRuleNamespaceURI.
    410         */
    411        public String getDigesterRuleNamespaceURI()
    412        {
    413            return digesterRuleNamespaceURI;
    414        }
    415
    416        /**
    417         * Sets the digesterRuleNamespaceURI.
    418         *
    419         * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use
    420         */
    421        public void setDigesterRuleNamespaceURI(String digesterRuleNamespaceURI)
    422        {
    423            this.digesterRuleNamespaceURI = digesterRuleNamespaceURI;
    424        }
    425
    426        /**
    427         * Configure the current digester to be namespace aware and to have
    428         * a Configuration object to which all of the other configurations
    429         * should be added
    430         *
    431         * @param digester The Digester to configure
    432         */
    433        private void configureNamespace(Digester digester)
    434        {
    435            if (getDigesterRuleNamespaceURI() != null)
    436            {
    437                digester.setNamespaceAware(true);
    438                digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI());
    439            }
    440            else
    441            {
    442                digester.setNamespaceAware(false);
    443            }
    444            digester.setValidating(false);
    445        }
    446
    447        /**
    448         * Returns the Base path from which this Configuration Factory operates.
    449         * This is never null. If you set the BasePath to null, then a base path
    450         * according to the configuration to load is returned.
    451         *
    452         * @return The base Path of this configuration factory.
    453         */
    454        public String getBasePath()
    455        {
    456            String path = StringUtils.isEmpty(basePath)
    457                    || DEF_BASE_PATH.equals(basePath) ? implicitBasePath : basePath;
    458            return StringUtils.isEmpty(path) ? DEF_BASE_PATH : path;
    459        }
    460
    461        /**
    462         * Sets the basePath for all file references from this Configuration Factory.
    463         * Normally a base path need not to be set because it is determined by
    464         * the location of the configuration file to load. All relative pathes in
    465         * this file are resolved relative to this file. Setting a base path makes
    466         * sense if such relative pathes should be otherwise resolved, e.g. if
    467         * the configuration file is loaded from the class path and all sub
    468         * configurations it refers to are stored in a special config directory.
    469         *
    470         * @param basePath The new basePath to set.
    471         */
    472        public void setBasePath(String basePath)
    473        {
    474            this.basePath = basePath;
    475        }
    476
    477        /**
    478         * A base class for digester factory classes. This base class maintains
    479         * a default class for the objects to be created.
    480         * There will be sub classes for specific configuration implementations.
    481         */
    482        public class DigesterConfigurationFactory extends AbstractObjectCreationFactory
    483        {
    484            /** Actual class to use. */
    485            private Class<?> clazz;
    486
    487            /**
    488             * Creates a new instance of {@code DigesterConfigurationFactory}.
    489             *
    490             * @param clazz the class which we should instantiate
    491             */
    492            public DigesterConfigurationFactory(Class<?> clazz)
    493            {
    494                this.clazz = clazz;
    495            }
    496
    497            /**
    498             * Creates an instance of the specified class.
    499             *
    500             * @param attribs the attributes (ignored)
    501             * @return the new object
    502             * @throws Exception if object creation fails
    503             */
    504            @Override
    505            public Object createObject(Attributes attribs) throws Exception
    506            {
    507                return clazz.newInstance();
    508            }
    509        }
    510
    511        /**
    512         * A tiny inner class that allows the Configuration Factory to
    513         * let the digester construct FileConfiguration objects
    514         * that already have the correct base Path set.
    515         *
    516         */
    517        public class FileConfigurationFactory extends DigesterConfigurationFactory
    518        {
    519            /**
    520             * C'tor
    521             *
    522             * @param clazz The class which we should instantiate.
    523             */
    524            public FileConfigurationFactory(Class<?> clazz)
    525            {
    526                super(clazz);
    527            }
    528
    529            /**
    530             * Gets called by the digester.
    531             *
    532             * @param attributes the actual attributes
    533             * @return the new object
    534             * @throws Exception Couldn't instantiate the requested object.
    535             */
    536            @Override
    537            public Object createObject(Attributes attributes) throws Exception
    538            {
    539                FileConfiguration conf = createConfiguration(attributes);
    540                conf.setBasePath(getBasePath());
    541                return conf;
    542            }
    543
    544            /**
    545             * Creates the object, a {@code FileConfiguration}.
    546             *
    547             * @param attributes the actual attributes
    548             * @return the file configuration
    549             * @throws Exception if the object could not be created
    550             */
    551            protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    552            {
    553                return (FileConfiguration) super.createObject(attributes);
    554            }
    555        }
    556
    557        /**
    558         * A factory that returns an XMLPropertiesConfiguration for .xml files
    559         * and a PropertiesConfiguration for the others.
    560         *
    561         * @since 1.2
    562         */
    563        public class PropertiesConfigurationFactory extends FileConfigurationFactory
    564        {
    565            /**
    566             * Creates a new instance of {@code PropertiesConfigurationFactory}.
    567             */
    568            public PropertiesConfigurationFactory()
    569            {
    570                super(null);
    571            }
    572
    573            /**
    574             * Creates the new configuration object. Based on the file name
    575             * provided in the attributes either a {@code PropertiesConfiguration}
    576             * or a {@code XMLPropertiesConfiguration} object will be
    577             * returned.
    578             *
    579             * @param attributes the attributes
    580             * @return the new configuration object
    581             * @throws Exception if an error occurs
    582             */
    583            @Override
    584            protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    585            {
    586                String filename = attributes.getValue(ATTR_FILENAME);
    587
    588                if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
    589                {
    590                    return new XMLPropertiesConfiguration();
    591                }
    592                else
    593                {
    594                    return new PropertiesConfiguration();
    595                }
    596            }
    597        }
    598
    599        /**
    600         * A factory that returns an XMLPropertyListConfiguration for .xml files
    601         * and a PropertyListConfiguration for the others.
    602         *
    603         * @since 1.2
    604         */
    605        public class PropertyListConfigurationFactory extends FileConfigurationFactory
    606        {
    607            /**
    608             * Creates a new instance of PropertyListConfigurationFactory</code>.
    609             */
    610            public PropertyListConfigurationFactory()
    611            {
    612                super(null);
    613            }
    614
    615            /**
    616             * Creates the new configuration object. Based on the file name
    617             * provided in the attributes either a {@code XMLPropertyListConfiguration}
    618             * or a {@code PropertyListConfiguration} object will be
    619             * returned.
    620             *
    621             * @param attributes the attributes
    622             * @return the new configuration object
    623             * @throws Exception if an error occurs
    624             */
    625            @Override
    626            protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    627            {
    628                String filename = attributes.getValue(ATTR_FILENAME);
    629
    630                if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
    631                {
    632                    return new XMLPropertyListConfiguration();
    633                }
    634                else
    635                {
    636                    return new PropertyListConfiguration();
    637                }
    638            }
    639        }
    640
    641        /**
    642         * A tiny inner class that allows the Configuration Factory to
    643         * let the digester construct JNDIConfiguration objects.
    644         */
    645        private class JNDIConfigurationFactory extends DigesterConfigurationFactory
    646        {
    647            /**
    648             * Creates a new instance of {@code JNDIConfigurationFactory}.
    649             */
    650            public JNDIConfigurationFactory()
    651            {
    652                super(JNDIConfiguration.class);
    653            }
    654        }
    655
    656        /**
    657         * A tiny inner class that allows the Configuration Factory to
    658         * let the digester construct SystemConfiguration objects.
    659         */
    660        private class SystemConfigurationFactory extends DigesterConfigurationFactory
    661        {
    662            /**
    663             * Creates a new instance of {@code SystemConfigurationFactory}.
    664             */
    665            public SystemConfigurationFactory()
    666            {
    667                super(SystemConfiguration.class);
    668            }
    669        }
    670
    671        /**
    672         * A simple data class that holds all information about a configuration
    673         * from the <code>&lt;additional&gt;</code> section.
    674         */
    675        public static class AdditionalConfigurationData
    676        {
    677            /** Stores the configuration object.*/
    678            private Configuration configuration;
    679
    680            /** Stores the location of this configuration in the global tree.*/
    681            private String at;
    682
    683            /**
    684             * Returns the value of the {@code at} attribute.
    685             *
    686             * @return the at attribute
    687             */
    688            public String getAt()
    689            {
    690                return at;
    691            }
    692
    693            /**
    694             * Sets the value of the {@code at} attribute.
    695             *
    696             * @param string the attribute value
    697             */
    698            public void setAt(String string)
    699            {
    700                at = string;
    701            }
    702
    703            /**
    704             * Returns the configuration object.
    705             *
    706             * @return the configuration
    707             */
    708            public Configuration getConfiguration()
    709            {
    710                return configuration;
    711            }
    712
    713            /**
    714             * Sets the configuration object. Note: Normally this method should be
    715             * named {@code setConfiguration()}, but the name
    716             * {@code addConfiguration()} is required by some of the digester
    717             * rules.
    718             *
    719             * @param config the configuration to set
    720             */
    721            public void addConfiguration(Configuration config)
    722            {
    723                configuration = config;
    724            }
    725        }
    726
    727        /**
    728         * An internally used helper class for constructing the composite
    729         * configuration object.
    730         */
    731        public static class ConfigurationBuilder
    732        {
    733            /** Stores the composite configuration.*/
    734            private CompositeConfiguration config;
    735
    736            /** Stores a collection with the configs from the additional section.*/
    737            private Collection<AdditionalConfigurationData> additionalConfigs;
    738
    739            /**
    740             * Creates a new instance of {@code ConfigurationBuilder}.
    741             */
    742            public ConfigurationBuilder()
    743            {
    744                config = new CompositeConfiguration();
    745                additionalConfigs = new LinkedList<AdditionalConfigurationData>();
    746            }
    747
    748            /**
    749             * Adds a new configuration to this object. This method is called by
    750             * Digester.
    751             *
    752             * @param conf the configuration to be added
    753             */
    754            public void addConfiguration(Configuration conf)
    755            {
    756                config.addConfiguration(conf);
    757            }
    758
    759            /**
    760             * Adds information about an additional configuration. This method is
    761             * called by Digester.
    762             *
    763             * @param data the data about the additional configuration
    764             */
    765            public void addAdditionalConfig(AdditionalConfigurationData data)
    766            {
    767                additionalConfigs.add(data);
    768            }
    769
    770            /**
    771             * Returns the final composite configuration.
    772             *
    773             * @return the final configuration object
    774             */
    775            public CompositeConfiguration getConfiguration()
    776            {
    777                if (!additionalConfigs.isEmpty())
    778                {
    779                    Configuration unionConfig = createAdditionalConfiguration(additionalConfigs);
    780                    if (unionConfig != null)
    781                    {
    782                        addConfiguration(unionConfig);
    783                    }
    784                    additionalConfigs.clear();
    785                }
    786
    787                return config;
    788            }
    789
    790            /**
    791             * Creates a configuration object with the union of all properties
    792             * defined in the <code>&lt;additional&gt;</code> section. This
    793             * implementation returns a {@code HierarchicalConfiguration}
    794             * object.
    795             *
    796             * @param configs a collection with
    797             * {@code AdditionalConfigurationData} objects
    798             * @return the union configuration (can be <b>null</b>)
    799             */
    800            protected Configuration createAdditionalConfiguration(Collection<AdditionalConfigurationData> configs)
    801            {
    802                HierarchicalConfiguration result = new HierarchicalConfiguration();
    803
    804                for (AdditionalConfigurationData cdata : configs)
    805                {
    806                    result.addNodes(cdata.getAt(),
    807                    createRootNode(cdata).getChildren());
    808                }
    809
    810                return result.isEmpty() ? null : result;
    811            }
    812
    813            /**
    814             * Creates a configuration root node for the specified configuration.
    815             *
    816             * @param cdata the configuration data object
    817             * @return a root node for this configuration
    818             */
    819            private HierarchicalConfiguration.Node createRootNode(AdditionalConfigurationData cdata)
    820            {
    821                if (cdata.getConfiguration() instanceof HierarchicalConfiguration)
    822                {
    823                    // we can directly use this configuration's root node
    824                    return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot();
    825                }
    826                else
    827                {
    828                    // transform configuration to a hierarchical root node
    829                    HierarchicalConfiguration hc = new HierarchicalConfiguration();
    830                    ConfigurationUtils.copy(cdata.getConfiguration(), hc);
    831                    return hc.getRoot();
    832                }
    833            }
    834        }
    835
    836        /**
    837         * A special implementation of Digester's {@code CallMethodRule} that
    838         * is internally used for calling a file configuration's {@code load()}
    839         * method. This class differs from its ancestor that it catches all occurring
    840         * exceptions when the specified method is called. It then checks whether
    841         * for the corresponding configuration the optional attribute is set. If
    842         * this is the case, the exception will simply be ignored.
    843         *
    844         * @since 1.4
    845         */
    846        private static class CallOptionalMethodRule extends CallMethodRule
    847        {
    848            /** A flag whether the optional attribute is set for this node. */
    849            private boolean optional;
    850
    851            /**
    852             * Creates a new instance of {@code CallOptionalMethodRule} and
    853             * sets the name of the method to invoke.
    854             *
    855             * @param methodName the name of the method
    856             */
    857            public CallOptionalMethodRule(String methodName)
    858            {
    859                super(methodName);
    860            }
    861
    862            /**
    863             * Checks if the optional attribute is set.
    864             *
    865             * @param attrs the attributes
    866             * @throws Exception if an error occurs
    867             */
    868            @Override
    869            public void begin(Attributes attrs) throws Exception
    870            {
    871                optional = attrs.getValue(ATTR_OPTIONAL) != null
    872                        && PropertyConverter.toBoolean(
    873                                attrs.getValue(ATTR_OPTIONAL)).booleanValue();
    874                super.begin(attrs);
    875            }
    876
    877            /**
    878             * Calls the method. If the optional attribute was set, occurring
    879             * exceptions will be ignored.
    880             *
    881             * @throws Exception if an error occurs
    882             */
    883            @Override
    884            public void end() throws Exception
    885            {
    886                try
    887                {
    888                    super.end();
    889                }
    890                catch (Exception ex)
    891                {
    892                    if (optional)
    893                    {
    894                        log.warn("Could not create optional configuration!", ex);
    895                    }
    896                    else
    897                    {
    898                        throw ex;
    899                    }
    900                }
    901            }
    902        }
    903    }