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