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