Coverage Report - org.apache.commons.configuration.ConfigurationFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
ConfigurationFactory
96%
88/91
100%
16/16
1,698
ConfigurationFactory$AdditionalConfigurationData
100%
7/7
N/A
1,698
ConfigurationFactory$CallOptionalMethodRule
100%
12/12
100%
6/6
1,698
ConfigurationFactory$ConfigurationBuilder
100%
24/24
100%
10/10
1,698
ConfigurationFactory$DigesterConfigurationFactory
100%
4/4
N/A
1,698
ConfigurationFactory$FileConfigurationFactory
100%
7/7
N/A
1,698
ConfigurationFactory$JNDIConfigurationFactory
100%
3/3
N/A
1,698
ConfigurationFactory$PropertiesConfigurationFactory
100%
7/7
75%
3/4
1,698
ConfigurationFactory$PropertyListConfigurationFactory
42%
3/7
0%
0/4
1,698
ConfigurationFactory$SystemConfigurationFactory
100%
3/3
N/A
1,698
 
 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.LinkedList;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.apache.commons.configuration.plist.PropertyListConfiguration;
 29  
 import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;
 30  
 import org.apache.commons.digester.AbstractObjectCreationFactory;
 31  
 import org.apache.commons.digester.CallMethodRule;
 32  
 import org.apache.commons.digester.Digester;
 33  
 import org.apache.commons.digester.ObjectCreationFactory;
 34  
 import org.apache.commons.digester.Substitutor;
 35  
 import org.apache.commons.digester.substitution.MultiVariableExpander;
 36  
 import org.apache.commons.digester.substitution.VariableSubstitutor;
 37  
 import org.apache.commons.digester.xmlrules.DigesterLoader;
 38  
 import org.apache.commons.lang.StringUtils;
 39  
 import org.apache.commons.logging.Log;
 40  
 import org.apache.commons.logging.LogFactory;
 41  
 import org.xml.sax.Attributes;
 42  
 import org.xml.sax.SAXException;
 43  
 
 44  
 /**
 45  
  * <p>
 46  
  * Factory class to create a CompositeConfiguration from a .xml file using
 47  
  * Digester.  By default it can handle the Configurations from commons-
 48  
  * configuration.  If you need to add your own, then you can pass in your own
 49  
  * digester rules to use.  It is also namespace aware, by providing a
 50  
  * digesterRuleNamespaceURI.
 51  
  * </p>
 52  
  * <p>
 53  
  * <em>Note:</em> Almost all of the features provided by this class and many
 54  
  * more are also available for the {@link DefaultConfigurationBuilder}
 55  
  * class. {@code DefaultConfigurationBuilder} also has a more robust
 56  
  * merge algorithm for constructing combined configurations. So it is
 57  
  * recommended to use this class instead of {@code ConfigurationFactory}.
 58  
  * </p>
 59  
  *
 60  
  * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
 61  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 62  
  * @version $Id: org.apache.commons.configuration.ConfigurationFactory.html 901807 2014-03-16 10:49:30Z oheger $
 63  
  * @deprecated Use {@link DefaultConfigurationBuilder} instead; this class
 64  
  * provides the same features as ConfigurationFactory plus some more; it can
 65  
  * also process the same configuration definition files.
 66  
  */
 67  
 @Deprecated
 68  8
 public class ConfigurationFactory
 69  
 {
 70  
     /** Constant for the root element in the info file.*/
 71  
     private static final String SEC_ROOT = "configuration/";
 72  
 
 73  
     /** Constant for the override section.*/
 74  
     private static final String SEC_OVERRIDE = SEC_ROOT + "override/";
 75  
 
 76  
     /** Constant for the additional section.*/
 77  
     private static final String SEC_ADDITIONAL = SEC_ROOT + "additional/";
 78  
 
 79  
     /** Constant for the optional attribute.*/
 80  
     private static final String ATTR_OPTIONAL = "optional";
 81  
 
 82  
     /** Constant for the fileName attribute.*/
 83  
     private static final String ATTR_FILENAME = "fileName";
 84  
 
 85  
     /** Constant for the load method.*/
 86  
     private static final String METH_LOAD = "load";
 87  
 
 88  
     /** Constant for the default base path (points to actual directory).*/
 89  
     private static final String DEF_BASE_PATH = ".";
 90  
 
 91  
     /** static logger */
 92  1
     private static Log log = LogFactory.getLog(ConfigurationFactory.class);
 93  
 
 94  
     /** The XML file with the details about the configuration to load */
 95  
     private String configurationFileName;
 96  
 
 97  
     /** The URL to the XML file with the details about the configuration to load. */
 98  
     private URL configurationURL;
 99  
 
 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  31
     {
 121  31
         setBasePath(DEF_BASE_PATH);
 122  31
     }
 123  
     /**
 124  
      * Constructor with ConfigurationFile Name passed
 125  
      *
 126  
      * @param configurationFileName The path to the configuration file
 127  
      */
 128  
     public ConfigurationFactory(String configurationFileName)
 129  2
     {
 130  2
         setConfigurationFileName(configurationFileName);
 131  2
     }
 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  29
         InputStream input = null;
 147  29
         ConfigurationBuilder builder = new ConfigurationBuilder();
 148  29
         URL url = getConfigurationURL();
 149  
         try
 150  
         {
 151  29
             if (url == null)
 152  
             {
 153  22
                 url = ConfigurationUtils.locate(implicitBasePath, getConfigurationFileName());
 154  
             }
 155  29
             input = url.openStream();
 156  
         }
 157  1
         catch (Exception e)
 158  
         {
 159  1
             log.error("Exception caught opening stream to URL", e);
 160  1
             throw new ConfigurationException("Exception caught opening stream to URL", e);
 161  28
         }
 162  
 
 163  28
         if (getDigesterRules() == null)
 164  
         {
 165  27
             digester = new Digester();
 166  27
             configureNamespace(digester);
 167  27
             initDefaultDigesterRules(digester);
 168  
         }
 169  
         else
 170  
         {
 171  1
             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  1
             configureNamespace(digester);
 175  
         }
 176  
 
 177  
         // Configure digester to always enable the context class loader
 178  28
         digester.setUseContextClassLoader(true);
 179  
         // Add a substitutor to resolve system properties
 180  28
         enableDigesterSubstitutor(digester);
 181  
         // Put the composite builder object below all of the other objects.
 182  28
         digester.push(builder);
 183  
         // Parse the input stream to configure our mappings
 184  
         try
 185  
         {
 186  28
             digester.parse(input);
 187  26
             input.close();
 188  
         }
 189  2
         catch (SAXException saxe)
 190  
         {
 191  2
             log.error("SAX Exception caught", saxe);
 192  2
             throw new ConfigurationException("SAX Exception caught", saxe);
 193  
         }
 194  0
         catch (IOException ioe)
 195  
         {
 196  0
             log.error("IO Exception caught", ioe);
 197  0
             throw new ConfigurationException("IO Exception caught", ioe);
 198  26
         }
 199  26
         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  22
         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  25
         File file = new File(configurationFileName).getAbsoluteFile();
 220  25
         this.configurationFileName = file.getName();
 221  25
         implicitBasePath = file.getParent();
 222  25
     }
 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  29
         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  8
         configurationURL = url;
 243  8
         implicitBasePath = url.toString();
 244  8
     }
 245  
 
 246  
     /**
 247  
      * Returns the digesterRules.
 248  
      *
 249  
      * @return URL
 250  
      */
 251  
     public URL getDigesterRules()
 252  
     {
 253  29
         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  1
         this.digesterRules = digesterRules;
 264  1
     }
 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  28
         Map<String, Object> systemProperties =
 278  
                 (Map<String, Object>) (Object) System.getProperties();
 279  28
         MultiVariableExpander expander = new MultiVariableExpander();
 280  28
         expander.addSource("$", systemProperties);
 281  
 
 282  
         // allow expansion in both xml attributes and element text
 283  28
         Substitutor substitutor = new VariableSubstitutor(expander);
 284  28
         digester.setSubstitutor(substitutor);
 285  28
     }
 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  27
         initDigesterSectionRules(digester, SEC_ROOT, false);
 299  27
         initDigesterSectionRules(digester, SEC_OVERRIDE, false);
 300  27
         initDigesterSectionRules(digester, SEC_ADDITIONAL, true);
 301  27
     }
 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  81
         setupDigesterInstance(
 315  
             digester,
 316  
             matchString + "properties",
 317  
             new PropertiesConfigurationFactory(),
 318  
             METH_LOAD,
 319  
             additional);
 320  
 
 321  81
         setupDigesterInstance(
 322  
             digester,
 323  
             matchString + "plist",
 324  
             new PropertyListConfigurationFactory(),
 325  
             METH_LOAD,
 326  
             additional);
 327  
 
 328  81
         setupDigesterInstance(
 329  
             digester,
 330  
             matchString + "xml",
 331  
             new FileConfigurationFactory(XMLConfiguration.class),
 332  
             METH_LOAD,
 333  
             additional);
 334  
 
 335  81
         setupDigesterInstance(
 336  
             digester,
 337  
             matchString + "hierarchicalXml",
 338  
             new FileConfigurationFactory(XMLConfiguration.class),
 339  
             METH_LOAD,
 340  
             additional);
 341  
 
 342  81
         setupDigesterInstance(
 343  
             digester,
 344  
             matchString + "jndi",
 345  
             new JNDIConfigurationFactory(),
 346  
             null,
 347  
             additional);
 348  
 
 349  81
         setupDigesterInstance(
 350  
             digester,
 351  
             matchString + "system",
 352  
             new SystemConfigurationFactory(),
 353  
             null,
 354  
             additional);
 355  81
     }
 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  486
         if (additional)
 376  
         {
 377  162
             setupUnionRules(digester, matchString);
 378  
         }
 379  
 
 380  486
         digester.addFactoryCreate(matchString, factory);
 381  486
         digester.addSetProperties(matchString);
 382  
 
 383  486
         if (method != null)
 384  
         {
 385  324
             digester.addRule(matchString, new CallOptionalMethodRule(method));
 386  
         }
 387  
 
 388  486
         digester.addSetNext(matchString, "addConfiguration", Configuration.class.getName());
 389  486
     }
 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  162
         digester.addObjectCreate(matchString,
 400  
         AdditionalConfigurationData.class);
 401  162
         digester.addSetProperties(matchString);
 402  162
         digester.addSetNext(matchString, "addAdditionalConfig",
 403  
         AdditionalConfigurationData.class.getName());
 404  162
     }
 405  
 
 406  
     /**
 407  
      * Returns the digesterRuleNamespaceURI.
 408  
      *
 409  
      * @return A String with the digesterRuleNamespaceURI.
 410  
      */
 411  
     public String getDigesterRuleNamespaceURI()
 412  
     {
 413  29
         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  1
         this.digesterRuleNamespaceURI = digesterRuleNamespaceURI;
 424  1
     }
 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  28
         if (getDigesterRuleNamespaceURI() != null)
 436  
         {
 437  1
             digester.setNamespaceAware(true);
 438  1
             digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI());
 439  
         }
 440  
         else
 441  
         {
 442  27
             digester.setNamespaceAware(false);
 443  
         }
 444  28
         digester.setValidating(false);
 445  28
     }
 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  61
         String path = StringUtils.isEmpty(basePath)
 457  
                 || DEF_BASE_PATH.equals(basePath) ? implicitBasePath : basePath;
 458  61
         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  35
         this.basePath = basePath;
 475  35
     }
 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  486
         {
 494  486
             this.clazz = clazz;
 495  486
         }
 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  21
             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  324
         {
 526  324
             super(clazz);
 527  324
         }
 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  55
             FileConfiguration conf = createConfiguration(attributes);
 540  55
             conf.setBasePath(getBasePath());
 541  55
             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  19
             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  81
         {
 570  81
             super(null);
 571  81
         }
 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  36
             String filename = attributes.getValue(ATTR_FILENAME);
 587  
 
 588  36
             if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
 589  
             {
 590  2
                 return new XMLPropertiesConfiguration();
 591  
             }
 592  
             else
 593  
             {
 594  34
                 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  81
         {
 612  81
             super(null);
 613  81
         }
 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  0
             String filename = attributes.getValue(ATTR_FILENAME);
 629  
 
 630  0
             if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
 631  
             {
 632  0
                 return new XMLPropertyListConfiguration();
 633  
             }
 634  
             else
 635  
             {
 636  0
                 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  81
         {
 652  81
             super(JNDIConfiguration.class);
 653  81
         }
 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  81
         {
 667  81
             super(SystemConfiguration.class);
 668  81
         }
 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  21
     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  20
             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  10
             at = string;
 701  10
         }
 702  
 
 703  
         /**
 704  
          * Returns the configuration object.
 705  
          *
 706  
          * @return the configuration
 707  
          */
 708  
         public Configuration getConfiguration()
 709  
         {
 710  40
             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  21
             configuration = config;
 724  21
         }
 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  29
         {
 744  29
             config = new CompositeConfiguration();
 745  29
             additionalConfigs = new LinkedList<AdditionalConfigurationData>();
 746  29
         }
 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  44
             config.addConfiguration(conf);
 757  44
         }
 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  21
             additionalConfigs.add(data);
 768  21
         }
 769  
 
 770  
         /**
 771  
          * Returns the final composite configuration.
 772  
          *
 773  
          * @return the final configuration object
 774  
          */
 775  
         public CompositeConfiguration getConfiguration()
 776  
         {
 777  26
             if (!additionalConfigs.isEmpty())
 778  
             {
 779  6
                 Configuration unionConfig = createAdditionalConfiguration(additionalConfigs);
 780  6
                 if (unionConfig != null)
 781  
                 {
 782  5
                     addConfiguration(unionConfig);
 783  
                 }
 784  6
                 additionalConfigs.clear();
 785  
             }
 786  
 
 787  26
             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  6
             HierarchicalConfiguration result = new HierarchicalConfiguration();
 803  
 
 804  6
             for (AdditionalConfigurationData cdata : configs)
 805  
             {
 806  20
                 result.addNodes(cdata.getAt(),
 807  
                 createRootNode(cdata).getChildren());
 808  20
             }
 809  
 
 810  6
             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  20
             if (cdata.getConfiguration() instanceof HierarchicalConfiguration)
 822  
             {
 823  
                 // we can directly use this configuration's root node
 824  12
                 return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot();
 825  
             }
 826  
             else
 827  
             {
 828  
                 // transform configuration to a hierarchical root node
 829  8
                 HierarchicalConfiguration hc = new HierarchicalConfiguration();
 830  8
                 ConfigurationUtils.copy(cdata.getConfiguration(), hc);
 831  8
                 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  324
             super(methodName);
 860  324
         }
 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  55
             optional = attrs.getValue(ATTR_OPTIONAL) != null
 872  
                     && PropertyConverter.toBoolean(
 873  
                             attrs.getValue(ATTR_OPTIONAL)).booleanValue();
 874  55
             super.begin(attrs);
 875  55
         }
 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  55
                 super.end();
 889  
             }
 890  9
             catch (Exception ex)
 891  
             {
 892  9
                 if (optional)
 893  
                 {
 894  8
                     log.warn("Could not create optional configuration!", ex);
 895  
                 }
 896  
                 else
 897  
                 {
 898  1
                     throw ex;
 899  
                 }
 900  46
             }
 901  54
         }
 902  
     }
 903  
 }