View Javadoc

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