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