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 package org.apache.commons.configuration; 018 019 import java.io.File; 020 import java.net.URL; 021 import java.util.ArrayList; 022 import java.util.Collections; 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 028 import org.apache.commons.configuration.beanutils.BeanDeclaration; 029 import org.apache.commons.configuration.beanutils.BeanFactory; 030 import org.apache.commons.configuration.beanutils.BeanHelper; 031 import org.apache.commons.configuration.beanutils.DefaultBeanFactory; 032 import org.apache.commons.configuration.beanutils.XMLBeanDeclaration; 033 import org.apache.commons.configuration.interpol.ConfigurationInterpolator; 034 import org.apache.commons.configuration.tree.ConfigurationNode; 035 import org.apache.commons.configuration.tree.DefaultExpressionEngine; 036 import org.apache.commons.configuration.tree.OverrideCombiner; 037 import org.apache.commons.configuration.tree.UnionCombiner; 038 import org.apache.commons.lang.text.StrLookup; 039 import org.apache.commons.logging.LogFactory; 040 041 /** 042 * <p> 043 * A factory class that creates a composite configuration from an XML based 044 * <em>configuration definition file</em>. 045 * </p> 046 * <p> 047 * This class provides an easy and flexible means for loading multiple 048 * configuration sources and combining the results into a single configuration 049 * object. The sources to be loaded are defined in an XML document that can 050 * contain certain tags representing the different supported configuration 051 * classes. If such a tag is found, the corresponding <code>Configuration</code> 052 * class is instantiated and initialized using the classes of the 053 * <code>beanutils</code> package (namely 054 * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code> 055 * will be used to extract the configuration's initialization parameters, which 056 * allows for complex initialization scenarios). 057 * </p> 058 * <p> 059 * It is also possible to add custom tags to the configuration definition file. 060 * For this purpose register your own <code>ConfigurationProvider</code> 061 * implementation for your tag using the <code>addConfigurationProvider()</code> 062 * method. This provider will then be called when the corresponding custom tag 063 * is detected. For the default configuration classes providers are already 064 * registered. 065 * </p> 066 * <p> 067 * The configuration definition file has the following basic structure: 068 * </p> 069 * <p> 070 * 071 * <pre> 072 * <configuration systemProperties="properties file name"> 073 * <header> 074 * <!-- Optional meta information about the composite configuration --> 075 * </header> 076 * <override> 077 * <!-- Declarations for override configurations --> 078 * </override> 079 * <additional> 080 * <!-- Declarations for union configurations --> 081 * </additional> 082 * </configuration> 083 * </pre> 084 * 085 * </p> 086 * <p> 087 * The name of the root element (here <code>configuration</code>) is 088 * arbitrary. The optional systemProperties attribute identifies the path to 089 * a property file containing properties that should be added to the system 090 * properties. If specified on the root element, the system properties are 091 * set before the rest of the configuration is processed. 092 * </p> 093 * <p> 094 * There are two sections (both of them are optional) for declaring 095 * <em>override</em> and <em>additional</em> configurations. Configurations 096 * in the former section are evaluated in the order of their declaration, and 097 * properties of configurations declared earlier hide those of configurations 098 * declared later. Configurations in the latter section are combined to a union 099 * configuration, i.e. all of their properties are added to a large hierarchical 100 * configuration. Configuration declarations that occur as direct children of 101 * the root element are treated as override declarations. 102 * </p> 103 * <p> 104 * Each configuration declaration consists of a tag whose name is associated 105 * with a <code>ConfigurationProvider</code>. This can be one of the 106 * predefined tags like <code>properties</code>, or <code>xml</code>, or 107 * a custom tag, for which a configuration provider was registered. Attributes 108 * and sub elements with specific initialization parameters can be added. There 109 * are some reserved attributes with a special meaning that can be used in every 110 * configuration declaration: 111 * </p> 112 * <p> 113 * <table border="1"> 114 * <tr> 115 * <th>Attribute</th> 116 * <th>Meaning</th> 117 * </tr> 118 * <tr> 119 * <td valign="top"><code>config-name</code></td> 120 * <td>Allows to specify a name for this configuration. This name can be used 121 * to obtain a reference to the configuration from the resulting combined 122 * configuration (see below).</td> 123 * </tr> 124 * <tr> 125 * <td valign="top"><code>config-at</code></td> 126 * <td>With this attribute an optional prefix can be specified for the 127 * properties of the corresponding configuration.</td> 128 * </tr> 129 * <tr> 130 * <td valign="top"><code>config-optional</code></td> 131 * <td>Declares a configuration as optional. This means that errors that occur 132 * when creating the configuration are ignored. (However 133 * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>s 134 * registered at the builder instance will get notified about this error: they 135 * receive an event of type <code>EVENT_ERR_LOAD_OPTIONAL</code>. The key 136 * property of this event contains the name of the optional configuration source 137 * that caused this problem.)</td> 138 * </tr> 139 * </table> 140 * </p> 141 * <p> 142 * The optional <em>header</em> section can contain some meta data about the 143 * created configuration itself. For instance, it is possible to set further 144 * properties of the <code>NodeCombiner</code> objects used for constructing 145 * the resulting configuration. 146 * </p> 147 * <p> 148 * The default configuration object returned by this builder is an instance of the 149 * <code>{@link CombinedConfiguration}</code> class. The return value of the 150 * <code>getConfiguration()</code> method can be casted to this type, and the 151 * <code>getConfiguration(boolean)</code> method directly declares 152 * <code>CombinedConfiguration</code> as return type. This allows for 153 * convenient access to the configuration objects maintained by the combined 154 * configuration (e.g. for updates of single configuration objects). It has also 155 * the advantage that the properties stored in all declared configuration 156 * objects are collected and transformed into a single hierarchical structure, 157 * which can be accessed using different expression engines. The actual CombinedConfiguration 158 * implementation can be overridden by specifying the class in the <em>config-class</em> 159 * attribute of the result element. 160 * </p> 161 * <p> 162 * Additional ConfigurationProviders can be added by configuring them in the <em>header</em> 163 * section. 164 * <pre> 165 * <providers> 166 * <provider config-tag="tag name" config-class="provider fully qualified class name"/> 167 * </providers> 168 * </pre> 169 * </p> 170 * <p> 171 * Additional variable resolvers can be added by configuring them in the <em>header</em> 172 * section. 173 * <pre> 174 * <lookups> 175 * <lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/> 176 * </lookups> 177 * </pre> 178 * </p> 179 * <p> 180 * All declared override configurations are directly added to the resulting 181 * combined configuration. If they are given names (using the 182 * <code>config-name</code> attribute), they can directly be accessed using 183 * the <code>getConfiguration(String)</code> method of 184 * <code>CombinedConfiguration</code>. The additional configurations are 185 * altogether added to another combined configuration, which uses a union 186 * combiner. Then this union configuration is added to the resulting combined 187 * configuration under the name defined by the <code>ADDITIONAL_NAME</code> 188 * constant. 189 * </p> 190 * <p> 191 * Implementation note: This class is not thread-safe. Especially the 192 * <code>getConfiguration()</code> methods should be called by a single thread 193 * only. 194 * </p> 195 * 196 * @since 1.3 197 * @author <a 198 * href="http://commons.apache.org/configuration/team-list.html">Commons 199 * Configuration team</a> 200 * @version $Id: DefaultConfigurationBuilder.java 727834 2008-12-18 22:16:32Z rgoers $ 201 */ 202 public class DefaultConfigurationBuilder extends XMLConfiguration implements 203 ConfigurationBuilder 204 { 205 /** 206 * Constant for the name of the additional configuration. If the 207 * configuration definition file contains an <code>additional</code> 208 * section, a special union configuration is created and added under this 209 * name to the resulting combined configuration. 210 */ 211 public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class 212 .getName() 213 + "/ADDITIONAL_CONFIG"; 214 215 /** 216 * Constant for the type of error events caused by optional configurations 217 * that cannot be loaded. 218 */ 219 public static final int EVENT_ERR_LOAD_OPTIONAL = 51; 220 221 /** Constant for the name of the configuration bean factory. */ 222 static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class 223 .getName() 224 + ".CONFIG_BEAN_FACTORY_NAME"; 225 226 /** Constant for the reserved name attribute. */ 227 static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 228 + XMLBeanDeclaration.RESERVED_PREFIX 229 + "name" 230 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 231 232 /** Constant for the name of the at attribute. */ 233 static final String ATTR_ATNAME = "at"; 234 235 /** Constant for the reserved at attribute. */ 236 static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 237 + XMLBeanDeclaration.RESERVED_PREFIX 238 + ATTR_ATNAME 239 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 240 241 /** Constant for the at attribute without the reserved prefix. */ 242 static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 243 + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 244 245 /** Constant for the name of the optional attribute. */ 246 static final String ATTR_OPTIONALNAME = "optional"; 247 248 /** Constant for the reserved optional attribute. */ 249 static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 250 + XMLBeanDeclaration.RESERVED_PREFIX 251 + ATTR_OPTIONALNAME 252 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 253 254 /** Constant for the optional attribute without the reserved prefix. */ 255 static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 256 + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 257 258 /** Constant for the file name attribute. */ 259 static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 260 + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 261 262 /** Constant for the forceCreate attribute. */ 263 static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START 264 + XMLBeanDeclaration.RESERVED_PREFIX 265 + "forceCreate" 266 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END; 267 268 /** 269 * Constant for the tag attribute for providers. 270 */ 271 static final String KEY_SYSTEM_PROPS = "[@systemProperties]"; 272 273 /** Constant for the name of the header section. */ 274 static final String SEC_HEADER = "header"; 275 276 /** Constant for an expression that selects the union configurations. */ 277 static final String KEY_UNION = "additional"; 278 279 /** An array with the names of top level configuration sections.*/ 280 static final String[] CONFIG_SECTIONS = { 281 "additional", "override", SEC_HEADER 282 }; 283 284 /** 285 * Constant for an expression that selects override configurations in the 286 * override section. 287 */ 288 static final String KEY_OVERRIDE = "override"; 289 290 /** 291 * Constant for the key that points to the list nodes definition of the 292 * override combiner. 293 */ 294 static final String KEY_OVERRIDE_LIST = SEC_HEADER 295 + ".combiner.override.list-nodes.node"; 296 297 /** 298 * Constant for the key that points to the list nodes definition of the 299 * additional combiner. 300 */ 301 static final String KEY_ADDITIONAL_LIST = SEC_HEADER 302 + ".combiner.additional.list-nodes.node"; 303 304 /** 305 * Constant for the key for defining providers in the configuration file. 306 */ 307 static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER 308 + ".providers.provider"; 309 310 /** 311 * Constant for the tag attribute for providers. 312 */ 313 static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]"; 314 315 /** 316 * Constant for the key for defining variable resolvers 317 */ 318 static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER 319 + ".lookups.lookup"; 320 321 /** 322 * Constant for the prefix attribute for lookups. 323 */ 324 static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]"; 325 326 /** 327 * Constant for the key of the result declaration. This key can point to a 328 * bean declaration, which defines properties of the resulting combined 329 * configuration. 330 */ 331 static final String KEY_RESULT = SEC_HEADER + ".result"; 332 333 /** Constant for the key of the combiner in the result declaration.*/ 334 static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner"; 335 336 /** Constant for the XML file extension. */ 337 static final String EXT_XML = ".xml"; 338 339 /** Constant for the provider for properties files. */ 340 private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider( 341 XMLPropertiesConfiguration.class, PropertiesConfiguration.class, 342 EXT_XML); 343 344 /** Constant for the provider for XML files. */ 345 private static final ConfigurationProvider XML_PROVIDER = new XMLConfigurationProvider(); 346 347 /** Constant for the provider for JNDI sources. */ 348 private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider( 349 JNDIConfiguration.class); 350 351 /** Constant for the provider for system properties. */ 352 private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider( 353 SystemConfiguration.class); 354 355 /** Constant for the provider for plist files. */ 356 private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider( 357 "org.apache.commons.configuration.plist.XMLPropertyListConfiguration", 358 "org.apache.commons.configuration.plist.PropertyListConfiguration", 359 EXT_XML); 360 361 /** Constant for the provider for configuration definition files.*/ 362 private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider(); 363 364 /** An array with the names of the default tags. */ 365 private static final String[] DEFAULT_TAGS = 366 {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"}; 367 368 /** An array with the providers for the default tags. */ 369 private static final ConfigurationProvider[] DEFAULT_PROVIDERS = 370 {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER, 371 SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER}; 372 373 /** 374 * The serial version UID. 375 */ 376 private static final long serialVersionUID = -3113777854714492123L; 377 378 /** Stores the configuration that is currently constructed.*/ 379 private CombinedConfiguration constructedConfiguration; 380 381 /** Stores a map with the registered configuration providers. */ 382 private Map providers; 383 384 /** Stores the base path to the configuration sources to load. */ 385 private String configurationBasePath; 386 387 /** 388 * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A 389 * configuration definition file is not yet loaded. Use the diverse setter 390 * methods provided by file based configurations to specify the 391 * configuration definition file. 392 */ 393 public DefaultConfigurationBuilder() 394 { 395 super(); 396 providers = new HashMap(); 397 registerDefaultProviders(); 398 registerBeanFactory(); 399 setLogger(LogFactory.getLog(getClass())); 400 addErrorLogListener(); // log errors per default 401 } 402 403 /** 404 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and 405 * sets the specified configuration definition file. 406 * 407 * @param file the configuration definition file 408 */ 409 public DefaultConfigurationBuilder(File file) 410 { 411 this(); 412 setFile(file); 413 } 414 415 /** 416 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and 417 * sets the specified configuration definition file. 418 * 419 * @param fileName the name of the configuration definition file 420 * @throws ConfigurationException if an error occurs when the file is loaded 421 */ 422 public DefaultConfigurationBuilder(String fileName) 423 throws ConfigurationException 424 { 425 this(); 426 setFileName(fileName); 427 } 428 429 /** 430 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and 431 * sets the specified configuration definition file. 432 * 433 * @param url the URL to the configuration definition file 434 * @throws ConfigurationException if an error occurs when the file is loaded 435 */ 436 public DefaultConfigurationBuilder(URL url) throws ConfigurationException 437 { 438 this(); 439 setURL(url); 440 } 441 442 /** 443 * Returns the base path for the configuration sources to load. This path is 444 * used to resolve relative paths in the configuration definition file. 445 * 446 * @return the base path for configuration sources 447 */ 448 public String getConfigurationBasePath() 449 { 450 return (configurationBasePath != null) ? configurationBasePath 451 : getBasePath(); 452 } 453 454 /** 455 * Sets the base path for the configuration sources to load. Normally a base 456 * path need not to be set because it is determined by the location of the 457 * configuration definition file to load. All relative paths in this file 458 * are resolved relative to this file. Setting a base path makes sense if 459 * such relative paths should be otherwise resolved, e.g. if the 460 * 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 configurationBasePath the new base path to set 464 */ 465 public void setConfigurationBasePath(String configurationBasePath) 466 { 467 this.configurationBasePath = configurationBasePath; 468 } 469 470 /** 471 * Adds a configuration provider for the specified tag. Whenever this tag is 472 * encountered in the configuration definition file this provider will be 473 * called to create the configuration object. 474 * 475 * @param tagName the name of the tag in the configuration definition file 476 * @param provider the provider for this tag 477 */ 478 public void addConfigurationProvider(String tagName, 479 ConfigurationProvider provider) 480 { 481 if (tagName == null) 482 { 483 throw new IllegalArgumentException("Tag name must not be null!"); 484 } 485 if (provider == null) 486 { 487 throw new IllegalArgumentException("Provider must not be null!"); 488 } 489 490 providers.put(tagName, provider); 491 } 492 493 /** 494 * Removes the configuration provider for the specified tag name. 495 * 496 * @param tagName the tag name 497 * @return the removed configuration provider or <b>null</b> if none was 498 * registered for that tag 499 */ 500 public ConfigurationProvider removeConfigurationProvider(String tagName) 501 { 502 return (ConfigurationProvider) providers.remove(tagName); 503 } 504 505 /** 506 * Returns the configuration provider for the given tag. 507 * 508 * @param tagName the name of the tag 509 * @return the provider that was registered for this tag or <b>null</b> if 510 * there is none 511 */ 512 public ConfigurationProvider providerForTag(String tagName) 513 { 514 return (ConfigurationProvider) providers.get(tagName); 515 } 516 517 /** 518 * Returns the configuration provided by this builder. Loads and parses the 519 * configuration definition file and creates instances for the declared 520 * configurations. 521 * 522 * @return the configuration 523 * @throws ConfigurationException if an error occurs 524 */ 525 public Configuration getConfiguration() throws ConfigurationException 526 { 527 return getConfiguration(true); 528 } 529 530 /** 531 * Returns the configuration provided by this builder. If the boolean 532 * parameter is <b>true</b>, the configuration definition file will be 533 * loaded. It will then be parsed, and instances for the declared 534 * configurations will be created. 535 * 536 * @param load a flag whether the configuration definition file should be 537 * loaded; a value of <b>false</b> would make sense if the file has already 538 * been created or its content was manipulated using some of the property 539 * accessor methods 540 * @return the configuration 541 * @throws ConfigurationException if an error occurs 542 */ 543 public CombinedConfiguration getConfiguration(boolean load) 544 throws ConfigurationException 545 { 546 if (load) 547 { 548 load(); 549 } 550 551 initSystemProperties(); 552 registerConfiguredProviders(); 553 registerConfiguredLookups(); 554 555 CombinedConfiguration result = createResultConfiguration(); 556 constructedConfiguration = result; 557 558 List overrides = fetchTopLevelOverrideConfigs(); 559 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE)); 560 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST); 561 562 List additionals = fetchChildConfigs(KEY_UNION); 563 if (!additionals.isEmpty()) 564 { 565 CombinedConfiguration addConfig = new CombinedConfiguration( 566 new UnionCombiner()); 567 result.addConfiguration(addConfig, ADDITIONAL_NAME); 568 initCombinedConfiguration(addConfig, additionals, 569 KEY_ADDITIONAL_LIST); 570 } 571 572 return result; 573 } 574 575 /** 576 * Creates the resulting combined configuration. This method is called by 577 * <code>getConfiguration()</code>. It checks whether the 578 * <code>header</code> section of the configuration definition file 579 * contains a <code>result</code> element. If this is the case, it will be 580 * used to initialize the properties of the newly created configuration 581 * object. 582 * 583 * @return the resulting configuration object 584 * @throws ConfigurationException if an error occurs 585 */ 586 protected CombinedConfiguration createResultConfiguration() 587 throws ConfigurationException 588 { 589 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true); 590 CombinedConfiguration result = (CombinedConfiguration) BeanHelper 591 .createBean(decl, CombinedConfiguration.class); 592 593 if (getMaxIndex(KEY_COMBINER) < 0) 594 { 595 // No combiner defined => set default 596 result.setNodeCombiner(new OverrideCombiner()); 597 } 598 599 return result; 600 } 601 602 /** 603 * Initializes a combined configuration for the configurations of a specific 604 * section. This method is called for the override and for the additional 605 * section (if it exists). 606 * 607 * @param config the configuration to be initialized 608 * @param containedConfigs the list with the declaratinos of the contained 609 * configurations 610 * @param keyListNodes a list with the declaration of list nodes 611 * @throws ConfigurationException if an error occurs 612 */ 613 protected void initCombinedConfiguration(CombinedConfiguration config, 614 List containedConfigs, String keyListNodes) throws ConfigurationException 615 { 616 List listNodes = getList(keyListNodes); 617 for (Iterator it = listNodes.iterator(); it.hasNext();) 618 { 619 config.getNodeCombiner().addListNode((String) it.next()); 620 } 621 622 for (Iterator it = containedConfigs.iterator(); it.hasNext();) 623 { 624 HierarchicalConfiguration conf = (HierarchicalConfiguration) it 625 .next(); 626 ConfigurationDeclaration decl = new ConfigurationDeclaration(this, 627 conf); 628 AbstractConfiguration newConf = createConfigurationAt(decl); 629 if (newConf != null) 630 { 631 config.addConfiguration(newConf, decl.getConfiguration() 632 .getString(ATTR_NAME), decl.getAt()); 633 } 634 } 635 } 636 637 /** 638 * Registers the default configuration providers supported by this class. 639 * This method will be called during initialization. It registers 640 * configuration providers for the tags that are supported by default. 641 */ 642 protected void registerDefaultProviders() 643 { 644 for (int i = 0; i < DEFAULT_TAGS.length; i++) 645 { 646 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]); 647 } 648 } 649 650 /** 651 * Registers providers defined in the configuration. 652 * 653 * @throws ConfigurationException if an error occurs 654 */ 655 protected void registerConfiguredProviders() throws ConfigurationException 656 { 657 List nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS); 658 for (Iterator it = nodes.iterator(); it.hasNext();) 659 { 660 HierarchicalConfiguration config = (HierarchicalConfiguration) it.next(); 661 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 662 String key = config.getString(KEY_PROVIDER_KEY); 663 addConfigurationProvider(key, (ConfigurationProvider) BeanHelper 664 .createBean(decl)); 665 } 666 } 667 668 /** 669 * Registers StrLookups defined in the configuration. 670 * 671 * @throws ConfigurationException if an error occurs 672 */ 673 protected void registerConfiguredLookups() throws ConfigurationException 674 { 675 List nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS); 676 for (Iterator it = nodes.iterator(); it.hasNext();) 677 { 678 HierarchicalConfiguration config = (HierarchicalConfiguration) it.next(); 679 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 680 String key = config.getString(KEY_LOOKUP_KEY); 681 ConfigurationInterpolator.registerGlobalLookup(key, (StrLookup) BeanHelper.createBean(decl)); 682 } 683 } 684 685 /** 686 * If a property file is configured add the properties to the System properties. 687 * @throws ConfigurationException if an error occurs. 688 */ 689 protected void initSystemProperties() throws ConfigurationException 690 { 691 String fileName = getString(KEY_SYSTEM_PROPS); 692 if (fileName != null) 693 { 694 try 695 { 696 SystemConfiguration.setSystemProperties(fileName); 697 } 698 catch (Exception ex) 699 { 700 throw new ConfigurationException("Error setting system properties from " + fileName, ex); 701 } 702 703 } 704 } 705 706 /** 707 * Performs interpolation. This method will not only take this configuration 708 * instance into account (which is the one that loaded the configuration 709 * definition file), but also the so far constructed combined configuration. 710 * So variables can be used that point to properties that are defined in 711 * configuration sources loaded by this builder. 712 * 713 * @param value the value to be interpolated 714 * @return the interpolated value 715 */ 716 protected Object interpolate(Object value) 717 { 718 Object result = super.interpolate(value); 719 if (constructedConfiguration != null) 720 { 721 result = constructedConfiguration.interpolate(result); 722 } 723 return result; 724 } 725 726 protected void fireError(int type, String propName, Object propValue, 727 Throwable ex) 728 { 729 // This method is only overridden to fix a mysterious MethodNotFound 730 // error in the test cases when run under a JDK 1.3. 731 super.fireError(type, propName, propValue, ex); 732 } 733 734 /** 735 * Creates a configuration object from the specified configuration 736 * declaration. 737 * 738 * @param decl the configuration declaration 739 * @return the new configuration object 740 * @throws ConfigurationException if an error occurs 741 */ 742 private AbstractConfiguration createConfigurationAt( 743 ConfigurationDeclaration decl) throws ConfigurationException 744 { 745 try 746 { 747 return (AbstractConfiguration) BeanHelper.createBean(decl); 748 } 749 catch (Exception ex) 750 { 751 // redirect to configuration exceptions 752 throw new ConfigurationException(ex); 753 } 754 } 755 756 /** 757 * Returns a list with <code>SubnodeConfiguration</code> objects for the 758 * child nodes of the specified configuration node. 759 * 760 * @param node the start node 761 * @return a list with subnode configurations for the node's children 762 */ 763 private List fetchChildConfigs(ConfigurationNode node) 764 { 765 List children = node.getChildren(); 766 List result = new ArrayList(children.size()); 767 for (Iterator it = children.iterator(); it.hasNext();) 768 { 769 result.add(createSubnodeConfiguration((Node) it.next())); 770 } 771 return result; 772 } 773 774 /** 775 * Returns a list with <code>SubnodeConfiguration</code> objects for the 776 * child nodes of the node specified by the given key. 777 * 778 * @param key the key (must define exactly one node) 779 * @return a list with subnode configurations for the node's children 780 */ 781 private List fetchChildConfigs(String key) 782 { 783 List nodes = fetchNodeList(key); 784 if (nodes.size() > 0) 785 { 786 return fetchChildConfigs((ConfigurationNode) nodes.get(0)); 787 } 788 else 789 { 790 return Collections.EMPTY_LIST; 791 } 792 } 793 794 /** 795 * Finds the override configurations that are defined as top level elements 796 * in the configuration definition file. This method will fetch the child 797 * elements of the root node and remove the nodes that represent other 798 * configuration sections. The remaining nodes are treated as definitions 799 * for override configurations. 800 * 801 * @return a list with subnode configurations for the top level override 802 * configurations 803 */ 804 private List fetchTopLevelOverrideConfigs() 805 { 806 List configs = fetchChildConfigs(getRootNode()); 807 for (Iterator it = configs.iterator(); it.hasNext();) 808 { 809 String nodeName = ((SubnodeConfiguration) it.next()).getRootNode() 810 .getName(); 811 for (int i = 0; i < CONFIG_SECTIONS.length; i++) 812 { 813 if (CONFIG_SECTIONS[i].equals(nodeName)) 814 { 815 it.remove(); 816 break; 817 } 818 } 819 } 820 return configs; 821 } 822 823 /** 824 * Registers the bean factory used by this class if necessary. This method 825 * is called by the constructor to ensure that the required bean factory is 826 * available. 827 */ 828 private void registerBeanFactory() 829 { 830 synchronized (DefaultConfigurationBuilder.class) 831 { 832 if (!BeanHelper.registeredFactoryNames().contains( 833 CONFIG_BEAN_FACTORY_NAME)) 834 { 835 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME, 836 new ConfigurationBeanFactory()); 837 } 838 } 839 } 840 841 /** 842 * <p> 843 * A base class for creating and initializing configuration sources. 844 * </p> 845 * <p> 846 * Concrete sub classes of this base class are responsible for creating 847 * specific <code>Configuration</code> objects for the tags in the 848 * configuration definition file. The configuration factory will parse the 849 * definition file and try to find a matching 850 * <code>ConfigurationProvider</code> for each encountered tag. This 851 * provider is then asked to create a corresponding 852 * <code>Configuration</code> object. It is up to a concrete 853 * implementation how this object is created and initialized. 854 * </p> 855 * <p> 856 * Note that at the moment only configuration classes derived from 857 * <code>{@link AbstractConfiguration}</code> are supported. 858 * </p> 859 */ 860 public static class ConfigurationProvider extends DefaultBeanFactory 861 { 862 /** Stores the class of the configuration to be created. */ 863 private Class configurationClass; 864 865 /** Stores the name of the configuration class to be created.*/ 866 private String configurationClassName; 867 868 /** 869 * Creates a new uninitialized instance of 870 * <code>ConfigurationProvider</code>. 871 */ 872 public ConfigurationProvider() 873 { 874 this((Class) null); 875 } 876 877 /** 878 * Creates a new instance of <code>ConfigurationProvider</code> and 879 * sets the class of the configuration created by this provider. 880 * 881 * @param configClass the configuration class 882 */ 883 public ConfigurationProvider(Class configClass) 884 { 885 setConfigurationClass(configClass); 886 } 887 888 /** 889 * Creates a new instance of <code>ConfigurationProvider</code> and 890 * sets the name of the class of the configuration created by this 891 * provider. 892 * 893 * @param configClassName the name of the configuration class 894 * @since 1.4 895 */ 896 public ConfigurationProvider(String configClassName) 897 { 898 setConfigurationClassName(configClassName); 899 } 900 901 /** 902 * Returns the class of the configuration returned by this provider. 903 * 904 * @return the class of the provided configuration 905 */ 906 public Class getConfigurationClass() 907 { 908 return configurationClass; 909 } 910 911 /** 912 * Sets the class of the configuration returned by this provider. 913 * 914 * @param configurationClass the configuration class 915 */ 916 public void setConfigurationClass(Class configurationClass) 917 { 918 this.configurationClass = configurationClass; 919 } 920 921 /** 922 * Returns the name of the configuration class returned by this 923 * provider. 924 * 925 * @return the configuration class name 926 * @since 1.4 927 */ 928 public String getConfigurationClassName() 929 { 930 return configurationClassName; 931 } 932 933 /** 934 * Sets the name of the configuration class returned by this provider. 935 * 936 * @param configurationClassName the name of the configuration class 937 * @since 1.4 938 */ 939 public void setConfigurationClassName(String configurationClassName) 940 { 941 this.configurationClassName = configurationClassName; 942 } 943 944 /** 945 * Returns the configuration. This method is called to fetch the 946 * configuration from the provider. This implementation will call the 947 * inherited <code>{@link 948 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object) 949 * createBean()}</code> method to create a new instance of the 950 * configuration class. 951 * 952 * @param decl the bean declaration with initialization parameters for 953 * the configuration 954 * @return the new configuration object 955 * @throws Exception if an error occurs 956 */ 957 public AbstractConfiguration getConfiguration( 958 ConfigurationDeclaration decl) throws Exception 959 { 960 return (AbstractConfiguration) createBean(fetchConfigurationClass(), 961 decl, null); 962 } 963 964 /** 965 * Returns an uninitialized configuration of the represented type. This 966 * method will be called for optional configurations when the 967 * <code>getConfiguration()</code> method caused an error and the 968 * <code>forceCreate</code> attribute is set. A concrete sub class can 969 * here try to create an uninitialized, empty configuration, which may 970 * be possible if the error was created during initialization. This base 971 * implementation just returns <b>null</b>. 972 * 973 * @param decl the bean declaration with initialization parameters for 974 * the configuration 975 * @return the new configuration object 976 * @throws Exception if an error occurs 977 * @since 1.4 978 */ 979 public AbstractConfiguration getEmptyConfiguration( 980 ConfigurationDeclaration decl) throws Exception 981 { 982 return null; 983 } 984 985 /** 986 * Returns the configuration class supported by this provider. If a 987 * class object was set, it is returned. Otherwise the method tries to 988 * resolve the class name. 989 * 990 * @return the class of the configuration to be created 991 * @since 1.4 992 */ 993 protected synchronized Class fetchConfigurationClass() throws Exception 994 { 995 if (getConfigurationClass() == null) 996 { 997 setConfigurationClass(loadClass(getConfigurationClassName())); 998 } 999 return getConfigurationClass(); 1000 } 1001 1002 /** 1003 * Loads the class with the specified name dynamically. If the class's 1004 * name is <b>null</b>, <b>null</b> will also be returned. 1005 * 1006 * @param className the name of the class to be loaded 1007 * @return the class object 1008 * @throws ClassNotFoundException if class loading fails 1009 * @since 1.4 1010 */ 1011 protected Class loadClass(String className) 1012 throws ClassNotFoundException 1013 { 1014 return (className != null) ? Class.forName(className, true, 1015 getClass().getClassLoader()) : null; 1016 } 1017 } 1018 1019 /** 1020 * <p> 1021 * A specialized <code>BeanDeclaration</code> implementation that 1022 * represents the declaration of a configuration source. 1023 * </p> 1024 * <p> 1025 * Instances of this class are able to extract all information about a 1026 * configuration source from the configuration definition file. The 1027 * declaration of a configuration source is very similar to a bean 1028 * declaration processed by <code>XMLBeanDeclaration</code>. There are 1029 * very few differences, e.g. some reserved attributes like 1030 * <code>optional</code> and <code>at</code> and the fact that a bean 1031 * factory is never needed. 1032 * </p> 1033 */ 1034 public static class ConfigurationDeclaration extends XMLBeanDeclaration 1035 { 1036 /** Stores a reference to the associated configuration builder. */ 1037 private DefaultConfigurationBuilder configurationBuilder; 1038 1039 /** 1040 * Creates a new instance of <code>ConfigurationDeclaration</code> and 1041 * initializes it. 1042 * 1043 * @param builder the associated configuration builder 1044 * @param config the configuration this declaration is based onto 1045 */ 1046 public ConfigurationDeclaration(DefaultConfigurationBuilder builder, 1047 HierarchicalConfiguration config) 1048 { 1049 super(config); 1050 configurationBuilder = builder; 1051 } 1052 1053 /** 1054 * Returns the associated configuration builder. 1055 * 1056 * @return the configuration builder 1057 */ 1058 public DefaultConfigurationBuilder getConfigurationBuilder() 1059 { 1060 return configurationBuilder; 1061 } 1062 1063 /** 1064 * Returns the value of the <code>at</code> attribute. 1065 * 1066 * @return the value of the <code>at</code> attribute (can be <b>null</b>) 1067 */ 1068 public String getAt() 1069 { 1070 String result = this.getConfiguration().getString(ATTR_AT_RES); 1071 return (result == null) ? this.getConfiguration().getString(ATTR_AT) 1072 : result; 1073 } 1074 1075 /** 1076 * Returns a flag whether this is an optional configuration. 1077 * 1078 * @return a flag if this declaration points to an optional 1079 * configuration 1080 */ 1081 public boolean isOptional() 1082 { 1083 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES, 1084 null); 1085 if (value == null) 1086 { 1087 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL, 1088 Boolean.FALSE); 1089 } 1090 return value.booleanValue(); 1091 } 1092 1093 /** 1094 * Returns a flag whether this configuration should always be created 1095 * and added to the resulting combined configuration. This flag is 1096 * evaluated only for optional configurations whose normal creation has 1097 * caused an error. If for such a configuration the 1098 * <code>forceCreate</code> attribute is set and the corresponding 1099 * configuration provider supports this mode, an empty configuration 1100 * will be created and added to the resulting combined configuration. 1101 * 1102 * @return the value of the <code>forceCreate</code> attribute 1103 * @since 1.4 1104 */ 1105 public boolean isForceCreate() 1106 { 1107 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false); 1108 } 1109 1110 /** 1111 * Returns the name of the bean factory. For configuration source 1112 * declarations always a reserved factory is used. This factory's name 1113 * is returned by this implementation. 1114 * 1115 * @return the name of the bean factory 1116 */ 1117 public String getBeanFactoryName() 1118 { 1119 return CONFIG_BEAN_FACTORY_NAME; 1120 } 1121 1122 /** 1123 * Returns the bean's class name. This implementation will always return 1124 * <b>null</b>. 1125 * 1126 * @return the name of the bean's class 1127 */ 1128 public String getBeanClassName() 1129 { 1130 return null; 1131 } 1132 1133 /** 1134 * Checks whether the given node is reserved. This method will take 1135 * further reserved attributes into account 1136 * 1137 * @param nd the node 1138 * @return a flag whether this node is reserved 1139 */ 1140 protected boolean isReservedNode(ConfigurationNode nd) 1141 { 1142 if (super.isReservedNode(nd)) 1143 { 1144 return true; 1145 } 1146 1147 return nd.isAttribute() 1148 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode() 1149 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME 1150 .equals(nd.getName()) && nd.getParentNode() 1151 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0)); 1152 } 1153 1154 /** 1155 * Performs interpolation. This implementation will delegate 1156 * interpolation to the configuration builder, which takes care that the 1157 * currently constructed configuration is taken into account, too. 1158 * 1159 * @param value the value to be interpolated 1160 * @return the interpolated value 1161 */ 1162 protected Object interpolate(Object value) 1163 { 1164 return getConfigurationBuilder().interpolate(value); 1165 } 1166 } 1167 1168 /** 1169 * A specialized <code>BeanFactory</code> implementation that handles 1170 * configuration declarations. This class will retrieve the correct 1171 * configuration provider and delegate the task of creating the 1172 * configuration to this object. 1173 */ 1174 static class ConfigurationBeanFactory implements BeanFactory 1175 { 1176 /** 1177 * Creates an instance of a bean class. This implementation expects that 1178 * the passed in bean declaration is a declaration for a configuration. 1179 * It will determine the responsible configuration provider and delegate 1180 * the call to this instance. If creation of the configuration fails 1181 * and the <code>optional</code> attribute is set, the exception will 1182 * be ignored. If the <code>forceCreate</code> attribute is set, too, 1183 * the provider is asked to create an empty configuration. A return 1184 * value of <b>null</b> means that no configuration could be created. 1185 * 1186 * @param beanClass the bean class (will be ignored) 1187 * @param data the declaration 1188 * @param param an additional parameter (will be ignored) 1189 * @return the newly created configuration 1190 * @throws Exception if an error occurs 1191 */ 1192 public Object createBean(Class beanClass, BeanDeclaration data, 1193 Object param) throws Exception 1194 { 1195 ConfigurationDeclaration decl = (ConfigurationDeclaration) data; 1196 String tagName = decl.getNode().getName(); 1197 ConfigurationProvider provider = decl.getConfigurationBuilder() 1198 .providerForTag(tagName); 1199 if (provider == null) 1200 { 1201 throw new ConfigurationRuntimeException( 1202 "No ConfigurationProvider registered for tag " 1203 + tagName); 1204 } 1205 1206 try 1207 { 1208 return provider.getConfiguration(decl); 1209 } 1210 catch (Exception ex) 1211 { 1212 // If this is an optional configuration, ignore the exception 1213 if (!decl.isOptional()) 1214 { 1215 throw ex; 1216 } 1217 else 1218 { 1219 // Notify registered error listeners 1220 decl.getConfigurationBuilder().fireError( 1221 EVENT_ERR_LOAD_OPTIONAL, 1222 decl.getConfiguration().getString(ATTR_NAME), null, 1223 ex); 1224 1225 if (decl.isForceCreate()) 1226 { 1227 try 1228 { 1229 return provider.getEmptyConfiguration(decl); 1230 } 1231 catch (Exception ex2) 1232 { 1233 // Ignore exception, return null in this case 1234 ; 1235 } 1236 } 1237 return null; 1238 } 1239 } 1240 } 1241 1242 /** 1243 * Returns the default class for this bean factory. 1244 * 1245 * @return the default class 1246 */ 1247 public Class getDefaultBeanClass() 1248 { 1249 // Here some valid class must be returned, otherwise BeanHelper 1250 // will complain that the bean's class cannot be determined 1251 return Configuration.class; 1252 } 1253 } 1254 1255 /** 1256 * A specialized provider implementation that deals with file based 1257 * configurations. Ensures that the base path is correctly set and that the 1258 * load() method gets called. 1259 */ 1260 public static class FileConfigurationProvider extends ConfigurationProvider 1261 { 1262 /** 1263 * Creates a new instance of <code>FileConfigurationProvider</code>. 1264 */ 1265 public FileConfigurationProvider() 1266 { 1267 super(); 1268 } 1269 1270 /** 1271 * Creates a new instance of <code>FileConfigurationProvider</code> 1272 * and sets the configuration class. 1273 * 1274 * @param configClass the class for the configurations to be created 1275 */ 1276 public FileConfigurationProvider(Class configClass) 1277 { 1278 super(configClass); 1279 } 1280 1281 /** 1282 * Creates a new instance of <code>FileConfigurationProvider</code> 1283 * and sets the configuration class name. 1284 * 1285 * @param configClassName the name of the configuration to be created 1286 * @since 1.4 1287 */ 1288 public FileConfigurationProvider(String configClassName) 1289 { 1290 super(configClassName); 1291 } 1292 1293 /** 1294 * Creates the configuration. After that <code>load()</code> will be 1295 * called. If this configuration is marked as optional, exceptions will 1296 * be ignored. 1297 * 1298 * @param decl the declaration 1299 * @return the new configuration 1300 * @throws Exception if an error occurs 1301 */ 1302 public AbstractConfiguration getConfiguration( 1303 ConfigurationDeclaration decl) throws Exception 1304 { 1305 AbstractConfiguration result = getEmptyConfiguration(decl); 1306 ((FileConfiguration) result).load(); 1307 return result; 1308 } 1309 1310 /** 1311 * Returns an uninitialized file configuration. This method will be 1312 * called for optional configurations when the 1313 * <code>getConfiguration()</code> method caused an error and the 1314 * <code>forceCreate</code> attribute is set. It will create the 1315 * configuration of the represented type, but the <code>load()</code> 1316 * method won't be called. This way non-existing configuration files can 1317 * be handled gracefully: If loading a the file fails, an empty 1318 * configuration will be created that is already configured with the 1319 * correct file name. 1320 * 1321 * @param decl the bean declaration with initialization parameters for 1322 * the configuration 1323 * @return the new configuration object 1324 * @throws Exception if an error occurs 1325 * @since 1.4 1326 */ 1327 public AbstractConfiguration getEmptyConfiguration( 1328 ConfigurationDeclaration decl) throws Exception 1329 { 1330 return super.getConfiguration(decl); 1331 } 1332 1333 /** 1334 * Initializes the bean instance. Ensures that the file configuration's 1335 * base path will be initialized with the base path of the factory so 1336 * that relative path names can be correctly resolved. 1337 * 1338 * @param bean the bean to be initialized 1339 * @param data the declaration 1340 * @throws Exception if an error occurs 1341 */ 1342 protected void initBeanInstance(Object bean, BeanDeclaration data) 1343 throws Exception 1344 { 1345 FileConfiguration config = (FileConfiguration) bean; 1346 config.setBasePath(((ConfigurationDeclaration) data) 1347 .getConfigurationBuilder().getConfigurationBasePath()); 1348 super.initBeanInstance(bean, data); 1349 } 1350 } 1351 1352 /** 1353 * A specialized configuration provider for XML configurations. This 1354 * implementation acts like a <code>FileConfigurationProvider</code>, but 1355 * it will copy all entity IDs that have been registered for the 1356 * configuration builder to the new XML configuration before it is loaded. 1357 * 1358 * @since 1.6 1359 */ 1360 public static class XMLConfigurationProvider extends FileConfigurationProvider 1361 { 1362 /** 1363 * Creates a new instance of <code>XMLConfigurationProvider</code>. 1364 */ 1365 public XMLConfigurationProvider() 1366 { 1367 super(XMLConfiguration.class); 1368 } 1369 1370 /** 1371 * Returns a new empty configuration instance. This implementation 1372 * performs some additional initialization specific to XML 1373 * configurations. 1374 * 1375 * @param decl the configuration declaration 1376 * @return the new configuration 1377 * @throws Exception if an error occurs 1378 */ 1379 public AbstractConfiguration getEmptyConfiguration( 1380 ConfigurationDeclaration decl) throws Exception 1381 { 1382 XMLConfiguration config = (XMLConfiguration) super 1383 .getEmptyConfiguration(decl); 1384 1385 // copy the registered entities 1386 DefaultConfigurationBuilder builder = decl 1387 .getConfigurationBuilder(); 1388 config.getRegisteredEntities().putAll( 1389 builder.getRegisteredEntities()); 1390 return config; 1391 } 1392 } 1393 1394 /** 1395 * A specialized configuration provider for file based configurations that 1396 * can handle configuration sources whose concrete type depends on the 1397 * extension of the file to be loaded. One example is the 1398 * <code>properties</code> tag: if the file ends with ".xml" a 1399 * XMLPropertiesConfiguration object must be created, otherwise a 1400 * PropertiesConfiguration object. 1401 */ 1402 static class FileExtensionConfigurationProvider extends 1403 FileConfigurationProvider 1404 { 1405 /** 1406 * Stores the class to be created when the file extension matches. 1407 */ 1408 private Class matchingClass; 1409 1410 /** 1411 * Stores the name of the class to be created when the file extension 1412 * matches. 1413 */ 1414 private String matchingClassName; 1415 1416 /** 1417 * Stores the class to be created when the file extension does not 1418 * match. 1419 */ 1420 private Class defaultClass; 1421 1422 /** 1423 * Stores the name of the class to be created when the file extension 1424 * does not match. 1425 */ 1426 private String defaultClassName; 1427 1428 /** Stores the file extension to be checked against. */ 1429 private String fileExtension; 1430 1431 /** 1432 * Creates a new instance of 1433 * <code>FileExtensionConfigurationProvider</code> and initializes it. 1434 * 1435 * @param matchingClass the class to be created when the file extension 1436 * matches 1437 * @param defaultClass the class to be created when the file extension 1438 * does not match 1439 * @param extension the file extension to be checked agains 1440 */ 1441 public FileExtensionConfigurationProvider(Class matchingClass, 1442 Class defaultClass, String extension) 1443 { 1444 this.matchingClass = matchingClass; 1445 this.defaultClass = defaultClass; 1446 fileExtension = extension; 1447 } 1448 1449 /** 1450 * Creates a new instance of 1451 * <code>FileExtensionConfigurationProvider</code> and initializes it 1452 * with the names of the classes to be created. 1453 * 1454 * @param matchingClassName the name of the class to be created when the 1455 * file extension matches 1456 * @param defaultClassName the name of the class to be created when the 1457 * file extension does not match 1458 * @param extension the file extension to be checked against 1459 * @since 1.4 1460 */ 1461 public FileExtensionConfigurationProvider(String matchingClassName, 1462 String defaultClassName, String extension) 1463 { 1464 this.matchingClassName = matchingClassName; 1465 this.defaultClassName = defaultClassName; 1466 fileExtension = extension; 1467 } 1468 1469 /** 1470 * Returns the matching class object, no matter whether it was defined 1471 * as a class or as a class name. 1472 * 1473 * @return the matching class object 1474 * @throws Exception if an error occurs 1475 * @since 1.4 1476 */ 1477 protected synchronized Class fetchMatchingClass() throws Exception 1478 { 1479 if (matchingClass == null) 1480 { 1481 matchingClass = loadClass(matchingClassName); 1482 } 1483 return matchingClass; 1484 } 1485 1486 /** 1487 * Returns the default class object, no matter whether it was defined as 1488 * a class or as a class name. 1489 * 1490 * @return the default class object 1491 * @throws Exception if an error occurs 1492 * @since 1.4 1493 */ 1494 protected synchronized Class fetchDefaultClass() throws Exception 1495 { 1496 if (defaultClass == null) 1497 { 1498 defaultClass = loadClass(defaultClassName); 1499 } 1500 return defaultClass; 1501 } 1502 1503 /** 1504 * Creates the configuration object. The class is determined by the file 1505 * name's extension. 1506 * 1507 * @param beanClass the class 1508 * @param data the bean declaration 1509 * @return the new bean 1510 * @throws Exception if an error occurs 1511 */ 1512 protected Object createBeanInstance(Class beanClass, 1513 BeanDeclaration data) throws Exception 1514 { 1515 String fileName = ((ConfigurationDeclaration) data) 1516 .getConfiguration().getString(ATTR_FILENAME); 1517 if (fileName != null 1518 && fileName.toLowerCase().trim().endsWith(fileExtension)) 1519 { 1520 return super.createBeanInstance(fetchMatchingClass(), data); 1521 } 1522 else 1523 { 1524 return super.createBeanInstance(fetchDefaultClass(), data); 1525 } 1526 } 1527 } 1528 1529 /** 1530 * A specialized configuration provider class that allows to include other 1531 * configuration definition files. 1532 */ 1533 static class ConfigurationBuilderProvider extends ConfigurationProvider 1534 { 1535 /** 1536 * Creates a new instance of <code>ConfigurationBuilderProvider</code>. 1537 */ 1538 public ConfigurationBuilderProvider() 1539 { 1540 super(DefaultConfigurationBuilder.class); 1541 } 1542 1543 /** 1544 * Creates the configuration. First creates a configuration builder 1545 * object. Then returns the configuration created by this builder. 1546 * 1547 * @param decl the configuration declaration 1548 * @return the configuration 1549 * @exception Exception if an error occurs 1550 */ 1551 public AbstractConfiguration getConfiguration( 1552 ConfigurationDeclaration decl) throws Exception 1553 { 1554 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super 1555 .getConfiguration(decl); 1556 return builder.getConfiguration(true); 1557 } 1558 1559 /** 1560 * Returns an empty configuration in case of an optional configuration 1561 * could not be created. This implementation returns an empty combined 1562 * configuration. 1563 * 1564 * @param decl the configuration declaration 1565 * @return the configuration 1566 * @exception Exception if an error occurs 1567 * @since 1.4 1568 */ 1569 public AbstractConfiguration getEmptyConfiguration( 1570 ConfigurationDeclaration decl) throws Exception 1571 { 1572 return new CombinedConfiguration(); 1573 } 1574 } 1575 }