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