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 }