    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.Iterator;
    026    import java.util.LinkedList;
    027    import java.util.Map;
    028
    029    import org.apache.commons.configuration.plist.PropertyListConfiguration;
    030    import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;
    031    import org.apache.commons.digester.AbstractObjectCreationFactory;
    032    import org.apache.commons.digester.CallMethodRule;
    033    import org.apache.commons.digester.Digester;
    034    import org.apache.commons.digester.ObjectCreationFactory;
    035    import org.apache.commons.digester.Substitutor;
    036    import org.apache.commons.digester.substitution.MultiVariableExpander;
    037    import org.apache.commons.digester.substitution.VariableSubstitutor;
    038    import org.apache.commons.digester.xmlrules.DigesterLoader;
    039    import org.apache.commons.lang.StringUtils;
    040    import org.apache.commons.logging.Log;
    041    import org.apache.commons.logging.LogFactory;
    042    import org.xml.sax.Attributes;
    043    import org.xml.sax.SAXException;
    044
    045    /**
    046     * <p>
    047     * Factory class to create a CompositeConfiguration from a .xml file using
    048     * Digester.  By default it can handle the Configurations from commons-
    049     * configuration.  If you need to add your own, then you can pass in your own
    050     * digester rules to use.  It is also namespace aware, by providing a
    051     * digesterRuleNamespaceURI.
    052     * </p>
    053     * <p>
    054     * <em>Note:</em> Almost all of the features provided by this class and many
    055     * more are also available for the <code>{@link DefaultConfigurationBuilder}</code>
    056     * class. <code>DefaultConfigurationBuilder</code> also has a more robust
    057     * merge algorithm for constructing combined configurations. So it is
    058     * recommended to use this class instead of <code>ConfigurationFactory</code>.
    059     * </p>
    060     *
    061     * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    062     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    063     * @version $Id: ConfigurationFactory.java 1161254 2011-08-24 19:54:07Z oheger $
    064     * @deprecated Use {@link DefaultConfigurationBuilder} instead; this class
    065     * provides the same features as ConfigurationFactory plus some more; it can
    066     * also process the same configuration definition files.
    067     */
    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         * explicitely 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            Map systemProperties = System.getProperties();
    274            MultiVariableExpander expander = new MultiVariableExpander();
    275            expander.addSource("$", systemProperties);
    276
    277            // allow expansion in both xml attributes and element text
    278            Substitutor substitutor = new VariableSubstitutor(expander);
    279            digester.setSubstitutor(substitutor);
    280        }
    281
    282        /**
    283         * Initializes the parsing rules for the default digester
    284         *
    285         * This allows the Configuration Factory to understand the default types:
    286         * Properties, XML and JNDI. Two special sections are introduced:
    287         * <code>&lt;override&gt;</code> and <code>&lt;additional&gt;</code>.
    288         *
    289         * @param digester The digester to configure
    290         */
    291        protected void initDefaultDigesterRules(Digester digester)
    292        {
    293            initDigesterSectionRules(digester, SEC_ROOT, false);
    294            initDigesterSectionRules(digester, SEC_OVERRIDE, false);
    295            initDigesterSectionRules(digester, SEC_ADDITIONAL, true);
    296        }
    297
    298        /**
    299         * Sets up digester rules for a specified section of the configuration
    300         * info file.
    301         *
    302         * @param digester the current digester instance
    303         * @param matchString specifies the section
    304         * @param additional a flag if rules for the additional section are to be
    305         * added
    306         */
    307        protected void initDigesterSectionRules(Digester digester, String matchString, boolean additional)
    308        {
    309            setupDigesterInstance(
    310                digester,
    311                matchString + "properties",
    312                new PropertiesConfigurationFactory(),
    313                METH_LOAD,
    314                additional);
    315
    316            setupDigesterInstance(
    317                digester,
    318                matchString + "plist",
    319                new PropertyListConfigurationFactory(),
    320                METH_LOAD,
    321                additional);
    322
    323            setupDigesterInstance(
    324                digester,
    325                matchString + "xml",
    326                new FileConfigurationFactory(XMLConfiguration.class),
    327                METH_LOAD,
    328                additional);
    329
    330            setupDigesterInstance(
    331                digester,
    332                matchString + "hierarchicalXml",
    333                new FileConfigurationFactory(XMLConfiguration.class),
    334                METH_LOAD,
    335                additional);
    336
    337            setupDigesterInstance(
    338                digester,
    339                matchString + "jndi",
    340                new JNDIConfigurationFactory(),
    341                null,
    342                additional);
    343
    344            setupDigesterInstance(
    345                digester,
    346                matchString + "system",
    347                new SystemConfigurationFactory(),
    348                null,
    349                additional);
    350        }
    351
    352        /**
    353         * Sets up digester rules for a configuration to be loaded.
    354         *
    355         * @param digester the current digester
    356         * @param matchString the pattern to match with this rule
    357         * @param factory an ObjectCreationFactory instance to use for creating new
    358         * objects
    359         * @param method the name of a method to be called or <b>null</b> for none
    360         * @param additional a flag if rules for the additional section are to be
    361         * added
    362         */
    363        protected void setupDigesterInstance(
    364                Digester digester,
    365                String matchString,
    366                ObjectCreationFactory factory,
    367                String method,
    368                boolean additional)
    369        {
    370            if (additional)
    371            {
    372                setupUnionRules(digester, matchString);
    373            }
    374
    375            digester.addFactoryCreate(matchString, factory);
    376            digester.addSetProperties(matchString);
    377
    378            if (method != null)
    379            {
    380                digester.addRule(matchString, new CallOptionalMethodRule(method));
    381            }
    382
    383            digester.addSetNext(matchString, "addConfiguration", Configuration.class.getName());
    384        }
    385
    386        /**
    387         * Sets up rules for configurations in the additional section.
    388         *
    389         * @param digester the current digester
    390         * @param matchString the pattern to match with this rule
    391         */
    392        protected void setupUnionRules(Digester digester, String matchString)
    393        {
    394            digester.addObjectCreate(matchString,
    395            AdditionalConfigurationData.class);
    396            digester.addSetProperties(matchString);
    397            digester.addSetNext(matchString, "addAdditionalConfig",
    398            AdditionalConfigurationData.class.getName());
    399        }
    400
    401        /**
    402         * Returns the digesterRuleNamespaceURI.
    403         *
    404         * @return A String with the digesterRuleNamespaceURI.
    405         */
    406        public String getDigesterRuleNamespaceURI()
    407        {
    408            return digesterRuleNamespaceURI;
    409        }
    410
    411        /**
    412         * Sets the digesterRuleNamespaceURI.
    413         *
    414         * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use
    415         */
    416        public void setDigesterRuleNamespaceURI(String digesterRuleNamespaceURI)
    417        {
    418            this.digesterRuleNamespaceURI = digesterRuleNamespaceURI;
    419        }
    420
    421        /**
    422         * Configure the current digester to be namespace aware and to have
    423         * a Configuration object to which all of the other configurations
    424         * should be added
    425         *
    426         * @param digester The Digester to configure
    427         */
    428        private void configureNamespace(Digester digester)
    429        {
    430            if (getDigesterRuleNamespaceURI() != null)
    431            {
    432                digester.setNamespaceAware(true);
    433                digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI());
    434            }
    435            else
    436            {
    437                digester.setNamespaceAware(false);
    438            }
    439            digester.setValidating(false);
    440        }
    441
    442        /**
    443         * Returns the Base path from which this Configuration Factory operates.
    444         * This is never null. If you set the BasePath to null, then a base path
    445         * according to the configuration to load is returned.
    446         *
    447         * @return The base Path of this configuration factory.
    448         */
    449        public String getBasePath()
    450        {
    451            String path = StringUtils.isEmpty(basePath)
    452                    || DEF_BASE_PATH.equals(basePath) ? implicitBasePath : basePath;
    453            return StringUtils.isEmpty(path) ? DEF_BASE_PATH : path;
    454        }
    455
    456        /**
    457         * Sets the basePath for all file references from this Configuration Factory.
    458         * Normally a base path need not to be set because it is determined by
    459         * the location of the configuration file to load. All relative pathes in
    460         * this file are resolved relative to this file. Setting a base path makes
    461         * sense if such relative pathes should be otherwise resolved, e.g. if
    462         * the configuration file is loaded from the class path and all sub
    463         * configurations it refers to are stored in a special config directory.
    464         *
    465         * @param basePath The new basePath to set.
    466         */
    467        public void setBasePath(String basePath)
    468        {
    469            this.basePath = basePath;
    470        }
    471
    472        /**
    473         * A base class for digester factory classes. This base class maintains
    474         * a default class for the objects to be created.
    475         * There will be sub classes for specific configuration implementations.
    476         */
    477        public class DigesterConfigurationFactory extends AbstractObjectCreationFactory
    478        {
    479            /** Actual class to use. */
    480            private Class clazz;
    481
    482            /**
    483             * Creates a new instance of <code>DigesterConfigurationFactory</code>.
    484             *
    485             * @param clazz the class which we should instantiate
    486             */
    487            public DigesterConfigurationFactory(Class clazz)
    488            {
    489                this.clazz = clazz;
    490            }
    491
    492            /**
    493             * Creates an instance of the specified class.
    494             *
    495             * @param attribs the attributes (ignored)
    496             * @return the new object
    497             * @throws Exception if object creation fails
    498             */
    499            public Object createObject(Attributes attribs) throws Exception
    500            {
    501                return clazz.newInstance();
    502            }
    503        }
    504
    505        /**
    506         * A tiny inner class that allows the Configuration Factory to
    507         * let the digester construct FileConfiguration objects
    508         * that already have the correct base Path set.
    509         *
    510         */
    511        public class FileConfigurationFactory extends DigesterConfigurationFactory
    512        {
    513            /**
    514             * C'tor
    515             *
    516             * @param clazz The class which we should instantiate.
    517             */
    518            public FileConfigurationFactory(Class clazz)
    519            {
    520                super(clazz);
    521            }
    522
    523            /**
    524             * Gets called by the digester.
    525             *
    526             * @param attributes the actual attributes
    527             * @return the new object
    528             * @throws Exception Couldn't instantiate the requested object.
    529             */
    530            public Object createObject(Attributes attributes) throws Exception
    531            {
    532                FileConfiguration conf = createConfiguration(attributes);
    533                conf.setBasePath(getBasePath());
    534                return conf;
    535            }
    536
    537            /**
    538             * Creates the object, a <code>FileConfiguration</code>.
    539             *
    540             * @param attributes the actual attributes
    541             * @return the file configuration
    542             * @throws Exception if the object could not be created
    543             */
    544            protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    545            {
    546                return (FileConfiguration) super.createObject(attributes);
    547            }
    548        }
    549
    550        /**
    551         * A factory that returns an XMLPropertiesConfiguration for .xml files
    552         * and a PropertiesConfiguration for the others.
    553         *
    554         * @since 1.2
    555         */
    556        public class PropertiesConfigurationFactory extends FileConfigurationFactory
    557        {
    558            /**
    559             * Creates a new instance of <code>PropertiesConfigurationFactory</code>.
    560             */
    561            public PropertiesConfigurationFactory()
    562            {
    563                super(null);
    564            }
    565
    566            /**
    567             * Creates the new configuration object. Based on the file name
    568             * provided in the attributes either a <code>PropertiesConfiguration</code>
    569             * or a <code>XMLPropertiesConfiguration</code> object will be
    570             * returned.
    571             *
    572             * @param attributes the attributes
    573             * @return the new configuration object
    574             * @throws Exception if an error occurs
    575             */
    576            protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    577            {
    578                String filename = attributes.getValue(ATTR_FILENAME);
    579
    580                if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
    581                {
    582                    return new XMLPropertiesConfiguration();
    583                }
    584                else
    585                {
    586                    return new PropertiesConfiguration();
    587                }
    588            }
    589        }
    590
    591        /**
    592         * A factory that returns an XMLPropertyListConfiguration for .xml files
    593         * and a PropertyListConfiguration for the others.
    594         *
    595         * @since 1.2
    596         */
    597        public class PropertyListConfigurationFactory extends FileConfigurationFactory
    598        {
    599            /**
    600             * Creates a new instance of <code>PropertyListConfigurationFactory</code>.
    601             */
    602            public PropertyListConfigurationFactory()
    603            {
    604                super(null);
    605            }
    606
    607            /**
    608             * Creates the new configuration object. Based on the file name
    609             * provided in the attributes either a <code>XMLPropertyListConfiguration</code>
    610             * or a <code>PropertyListConfiguration</code> object will be
    611             * returned.
    612             *
    613             * @param attributes the attributes
    614             * @return the new configuration object
    615             * @throws Exception if an error occurs
    616             */
    617            protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    618            {
    619                String filename = attributes.getValue(ATTR_FILENAME);
    620
    621                if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
    622                {
    623                    return new XMLPropertyListConfiguration();
    624                }
    625                else
    626                {
    627                    return new PropertyListConfiguration();
    628                }
    629            }
    630        }
    631
    632        /**
    633         * A tiny inner class that allows the Configuration Factory to
    634         * let the digester construct JNDIConfiguration objects.
    635         */
    636        private class JNDIConfigurationFactory extends DigesterConfigurationFactory
    637        {
    638            /**
    639             * Creates a new instance of <code>JNDIConfigurationFactory</code>.
    640             */
    641            public JNDIConfigurationFactory()
    642            {
    643                super(JNDIConfiguration.class);
    644            }
    645        }
    646
    647        /**
    648         * A tiny inner class that allows the Configuration Factory to
    649         * let the digester construct SystemConfiguration objects.
    650         */
    651        private class SystemConfigurationFactory extends DigesterConfigurationFactory
    652        {
    653            /**
    654             * Creates a new instance of <code>SystemConfigurationFactory</code>.
    655             */
    656            public SystemConfigurationFactory()
    657            {
    658                super(SystemConfiguration.class);
    659            }
    660        }
    661
    662        /**
    663         * A simple data class that holds all information about a configuration
    664         * from the <code>&lt;additional&gt;</code> section.
    665         */
    666        public static class AdditionalConfigurationData
    667        {
    668            /** Stores the configuration object.*/
    669            private Configuration configuration;
    670
    671            /** Stores the location of this configuration in the global tree.*/
    672            private String at;
    673
    674            /**
    675             * Returns the value of the <code>at</code> attribute.
    676             *
    677             * @return the at attribute
    678             */
    679            public String getAt()
    680            {
    681                return at;
    682            }
    683
    684            /**
    685             * Sets the value of the <code>at</code> attribute.
    686             *
    687             * @param string the attribute value
    688             */
    689            public void setAt(String string)
    690            {
    691                at = string;
    692            }
    693
    694            /**
    695             * Returns the configuration object.
    696             *
    697             * @return the configuration
    698             */
    699            public Configuration getConfiguration()
    700            {
    701                return configuration;
    702            }
    703
    704            /**
    705             * Sets the configuration object. Note: Normally this method should be
    706             * named <code>setConfiguration()</code>, but the name
    707             * <code>addConfiguration()</code> is required by some of the digester
    708             * rules.
    709             *
    710             * @param config the configuration to set
    711             */
    712            public void addConfiguration(Configuration config)
    713            {
    714                configuration = config;
    715            }
    716        }
    717
    718        /**
    719         * An internally used helper class for constructing the composite
    720         * configuration object.
    721         */
    722        public static class ConfigurationBuilder
    723        {
    724            /** Stores the composite configuration.*/
    725            private CompositeConfiguration config;
    726
    727            /** Stores a collection with the configs from the additional section.*/
    728            private Collection additionalConfigs;
    729
    730            /**
    731             * Creates a new instance of <code>ConfigurationBuilder</code>.
    732             */
    733            public ConfigurationBuilder()
    734            {
    735                config = new CompositeConfiguration();
    736                additionalConfigs = new LinkedList();
    737            }
    738
    739            /**
    740             * Adds a new configuration to this object. This method is called by
    741             * Digester.
    742             *
    743             * @param conf the configuration to be added
    744             */
    745            public void addConfiguration(Configuration conf)
    746            {
    747                config.addConfiguration(conf);
    748            }
    749
    750            /**
    751             * Adds information about an additional configuration. This method is
    752             * called by Digester.
    753             *
    754             * @param data the data about the additional configuration
    755             */
    756            public void addAdditionalConfig(AdditionalConfigurationData data)
    757            {
    758                additionalConfigs.add(data);
    759            }
    760
    761            /**
    762             * Returns the final composite configuration.
    763             *
    764             * @return the final configuration object
    765             */
    766            public CompositeConfiguration getConfiguration()
    767            {
    768                if (!additionalConfigs.isEmpty())
    769                {
    770                    Configuration unionConfig = createAdditionalConfiguration(additionalConfigs);
    771                    if (unionConfig != null)
    772                    {
    773                        addConfiguration(unionConfig);
    774                    }
    775                    additionalConfigs.clear();
    776                }
    777
    778                return config;
    779            }
    780
    781            /**
    782             * Creates a configuration object with the union of all properties
    783             * defined in the <code>&lt;additional&gt;</code> section. This
    784             * implementation returns a <code>HierarchicalConfiguration</code>
    785             * object.
    786             *
    787             * @param configs a collection with
    788             * <code>AdditionalConfigurationData</code> objects
    789             * @return the union configuration (can be <b>null</b>)
    790             */
    791            protected Configuration createAdditionalConfiguration(Collection configs)
    792            {
    793                HierarchicalConfiguration result = new HierarchicalConfiguration();
    794
    795                for (Iterator it = configs.iterator(); it.hasNext();)
    796                {
    797                    AdditionalConfigurationData cdata =
    798                    (AdditionalConfigurationData) it.next();
    799                    result.addNodes(cdata.getAt(),
    800                    createRootNode(cdata).getChildren());
    801                }
    802
    803                return result.isEmpty() ? null : result;
    804            }
    805
    806            /**
    807             * Creates a configuration root node for the specified configuration.
    808             *
    809             * @param cdata the configuration data object
    810             * @return a root node for this configuration
    811             */
    812            private HierarchicalConfiguration.Node createRootNode(AdditionalConfigurationData cdata)
    813            {
    814                if (cdata.getConfiguration() instanceof HierarchicalConfiguration)
    815                {
    816                    // we can directly use this configuration's root node
    817                    return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot();
    818                }
    819                else
    820                {
    821                    // transform configuration to a hierarchical root node
    822                    HierarchicalConfiguration hc = new HierarchicalConfiguration();
    823                    ConfigurationUtils.copy(cdata.getConfiguration(), hc);
    824                    return hc.getRoot();
    825                }
    826            }
    827        }
    828
    829        /**
    830         * A special implementation of Digester's <code>CallMethodRule</code> that
    831         * is internally used for calling a file configuration's <code>load()</code>
    832         * method. This class difers from its ancestor that it catches all occuring
    833         * exceptions when the specified method is called. It then checks whether
    834         * for the corresponding configuration the optional attribute is set. If
    835         * this is the case, the exception will simply be ignored.
    836         *
    837         * @since 1.4
    838         */
    839        private static class CallOptionalMethodRule extends CallMethodRule
    840        {
    841            /** A flag whether the optional attribute is set for this node. */
    842            private boolean optional;
    843
    844            /**
    845             * Creates a new instance of <code>CallOptionalMethodRule</code> and
    846             * sets the name of the method to invoke.
    847             *
    848             * @param methodName the name of the method
    849             */
    850            public CallOptionalMethodRule(String methodName)
    851            {
    852                super(methodName);
    853            }
    854
    855            /**
    856             * Checks if the optional attribute is set.
    857             *
    858             * @param attrs the attributes
    859             * @throws Exception if an error occurs
    860             */
    861            public void begin(Attributes attrs) throws Exception
    862            {
    863                optional = attrs.getValue(ATTR_OPTIONAL) != null
    864                        && PropertyConverter.toBoolean(
    865                                attrs.getValue(ATTR_OPTIONAL)).booleanValue();
    866                super.begin(attrs);
    867            }
    868
    869            /**
    870             * Calls the method. If the optional attribute was set, occurring
    871             * exceptions will be ignored.
    872             *
    873             * @throws Exception if an error occurs
    874             */
    875            public void end() throws Exception
    876            {
    877                try
    878                {
    879                    super.end();
    880                }
    881                catch (Exception ex)
    882                {
    883                    if (optional)
    884                    {
    885                        log.warn("Could not create optional configuration!", ex);
    886                    }
    887                    else
    888                    {
    889                        throw ex;
    890                    }
    891                }
    892            }
    893        }
    894    }