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.LinkedList;
026 import java.util.Map;
027
028 import org.apache.commons.configuration.plist.PropertyListConfiguration;
029 import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;
030 import org.apache.commons.digester.AbstractObjectCreationFactory;
031 import org.apache.commons.digester.CallMethodRule;
032 import org.apache.commons.digester.Digester;
033 import org.apache.commons.digester.ObjectCreationFactory;
034 import org.apache.commons.digester.Substitutor;
035 import org.apache.commons.digester.substitution.MultiVariableExpander;
036 import org.apache.commons.digester.substitution.VariableSubstitutor;
037 import org.apache.commons.digester.xmlrules.DigesterLoader;
038 import org.apache.commons.lang.StringUtils;
039 import org.apache.commons.logging.Log;
040 import org.apache.commons.logging.LogFactory;
041 import org.xml.sax.Attributes;
042 import org.xml.sax.SAXException;
043
044 /**
045 * <p>
046 * Factory class to create a CompositeConfiguration from a .xml file using
047 * Digester. By default it can handle the Configurations from commons-
048 * configuration. If you need to add your own, then you can pass in your own
049 * digester rules to use. It is also namespace aware, by providing a
050 * digesterRuleNamespaceURI.
051 * </p>
052 * <p>
053 * <em>Note:</em> Almost all of the features provided by this class and many
054 * more are also available for the {@link DefaultConfigurationBuilder}
055 * class. {@code DefaultConfigurationBuilder} also has a more robust
056 * merge algorithm for constructing combined configurations. So it is
057 * recommended to use this class instead of {@code ConfigurationFactory}.
058 * </p>
059 *
060 * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
061 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
062 * @version $Id: ConfigurationFactory.java 1209685 2011-12-02 20:47:44Z oheger $
063 * @deprecated Use {@link DefaultConfigurationBuilder} instead; this class
064 * provides the same features as ConfigurationFactory plus some more; it can
065 * also process the same configuration definition files.
066 */
067 @Deprecated
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 * 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><override></code> and <code><additional></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><additional></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><additional></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 }