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} 059 * class is instantiated and initialized using the classes of the 060 * {@code beanutils} package (namely 061 * {@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration} 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} 068 * implementation for your tag using the {@code addConfigurationProvider()} 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}) 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}. This can be one of the 113 * predefined tags like {@code properties}, or {@code xml}, 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}</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}</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}</td> 138 * <td>Declares a configuration as optional. This means that errors that occur 139 * when creating the configuration are ignored. (However 140 * {@link org.apache.commons.configuration.event.ConfigurationErrorListener}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}. 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} 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 * {@link CombinedConfiguration} class. The return value of the 157 * {@code getConfiguration()} method can be casted to this type, and the 158 * {@code getConfiguration(boolean)} method directly declares 159 * {@code CombinedConfiguration} 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} attribute), they can directly be accessed using 200 * the {@code getConfiguration(String)} method of 201 * {@code CombinedConfiguration}. 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} 205 * constant. 206 * </p> 207 * <p> 208 * Implementation note: This class is not thread-safe. Especially the 209 * {@code getConfiguration()} 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 1208782 2011-11-30 21:12:00Z 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} 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<String, ConfigurationProvider> 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}. 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<String, ConfigurationProvider>(); 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} 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} 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} 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<SubnodeConfiguration> overrides = fetchTopLevelOverrideConfigs(); 600 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE)); 601 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST); 602 603 List<SubnodeConfiguration> 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()}. It checks whether the 618 * {@code header} section of the configuration definition file 619 * contains a {@code result} 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} 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} 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} 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<? extends HierarchicalConfiguration> containedConfigs, 681 String keyListNodes) throws ConfigurationException 682 { 683 List<Object> listNodes = getList(keyListNodes); 684 for (Object listNode : listNodes) 685 { 686 config.getNodeCombiner().addListNode((String) listNode); 687 } 688 689 for (HierarchicalConfiguration conf : containedConfigs) 690 { 691 ConfigurationDeclaration decl = new ConfigurationDeclaration(this, 692 conf); 693 if (getLogger().isDebugEnabled()) 694 { 695 getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name " 696 + decl.getConfiguration().getString(ATTR_NAME)); 697 } 698 AbstractConfiguration newConf = createConfigurationAt(decl); 699 if (newConf != null) 700 { 701 config.addConfiguration(newConf, decl.getConfiguration() 702 .getString(ATTR_NAME), decl.getAt()); 703 } 704 } 705 } 706 707 /** 708 * Registers the default configuration providers supported by this class. 709 * This method will be called during initialization. It registers 710 * configuration providers for the tags that are supported by default. 711 */ 712 protected void registerDefaultProviders() 713 { 714 for (int i = 0; i < DEFAULT_TAGS.length; i++) 715 { 716 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]); 717 } 718 } 719 720 /** 721 * Registers providers defined in the configuration. 722 * 723 * @throws ConfigurationException if an error occurs 724 */ 725 protected void registerConfiguredProviders() throws ConfigurationException 726 { 727 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS); 728 for (HierarchicalConfiguration config : nodes) 729 { 730 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 731 String key = config.getString(KEY_PROVIDER_KEY); 732 addConfigurationProvider(key, (ConfigurationProvider) BeanHelper 733 .createBean(decl)); 734 } 735 } 736 737 /** 738 * Registers StrLookups defined in the configuration. 739 * 740 * @throws ConfigurationException if an error occurs 741 */ 742 protected void registerConfiguredLookups() throws ConfigurationException 743 { 744 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS); 745 for (HierarchicalConfiguration config : nodes) 746 { 747 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 748 String key = config.getString(KEY_LOOKUP_KEY); 749 StrLookup lookup = (StrLookup) BeanHelper.createBean(decl); 750 BeanHelper.setProperty(lookup, "configuration", this); 751 ConfigurationInterpolator.registerGlobalLookup(key, lookup); 752 this.getInterpolator().registerLookup(key, lookup); 753 } 754 } 755 756 protected void initFileSystem() throws ConfigurationException 757 { 758 if (getMaxIndex(FILE_SYSTEM) == 0) 759 { 760 HierarchicalConfiguration config = configurationAt(FILE_SYSTEM); 761 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 762 setFileSystem((FileSystem) BeanHelper.createBean(decl)); 763 } 764 } 765 766 /** 767 * If a property file is configured add the properties to the System properties. 768 * @throws ConfigurationException if an error occurs. 769 */ 770 protected void initSystemProperties() throws ConfigurationException 771 { 772 String fileName = getString(KEY_SYSTEM_PROPS); 773 if (fileName != null) 774 { 775 try 776 { 777 SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName); 778 } 779 catch (Exception ex) 780 { 781 throw new ConfigurationException("Error setting system properties from " + fileName, ex); 782 } 783 784 } 785 } 786 787 protected void configureEntityResolver() throws ConfigurationException 788 { 789 if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0) 790 { 791 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true); 792 EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class); 793 BeanHelper.setProperty(resolver, "fileSystem", getFileSystem()); 794 BeanHelper.setProperty(resolver, "baseDir", getBasePath()); 795 BeanHelper.setProperty(resolver, "substitutor", getSubstitutor()); 796 setEntityResolver(resolver); 797 } 798 } 799 800 /** 801 * Performs interpolation. This method will not only take this configuration 802 * instance into account (which is the one that loaded the configuration 803 * definition file), but also the so far constructed combined configuration. 804 * So variables can be used that point to properties that are defined in 805 * configuration sources loaded by this builder. 806 * 807 * @param value the value to be interpolated 808 * @return the interpolated value 809 */ 810 @Override 811 protected Object interpolate(Object value) 812 { 813 Object result = super.interpolate(value); 814 if (constructedConfiguration != null) 815 { 816 result = constructedConfiguration.interpolate(result); 817 } 818 return result; 819 } 820 821 /** 822 * Creates a configuration object from the specified configuration 823 * declaration. 824 * 825 * @param decl the configuration declaration 826 * @return the new configuration object 827 * @throws ConfigurationException if an error occurs 828 */ 829 private AbstractConfiguration createConfigurationAt( 830 ConfigurationDeclaration decl) throws ConfigurationException 831 { 832 try 833 { 834 return (AbstractConfiguration) BeanHelper.createBean(decl); 835 } 836 catch (Exception ex) 837 { 838 // redirect to configuration exceptions 839 throw new ConfigurationException(ex); 840 } 841 } 842 843 /** 844 * Returns a list with {@code SubnodeConfiguration} objects for the 845 * child nodes of the specified configuration node. 846 * 847 * @param node the start node 848 * @return a list with subnode configurations for the node's children 849 */ 850 private List<SubnodeConfiguration> fetchChildConfigs(ConfigurationNode node) 851 { 852 List<ConfigurationNode> children = node.getChildren(); 853 List<SubnodeConfiguration> result = new ArrayList<SubnodeConfiguration>(children.size()); 854 for (ConfigurationNode child : children) 855 { 856 result.add(createSubnodeConfiguration(child)); 857 } 858 return result; 859 } 860 861 /** 862 * Returns a list with {@code SubnodeConfiguration} objects for the 863 * child nodes of the node specified by the given key. 864 * 865 * @param key the key (must define exactly one node) 866 * @return a list with subnode configurations for the node's children 867 */ 868 private List<SubnodeConfiguration> fetchChildConfigs(String key) 869 { 870 List<ConfigurationNode> nodes = fetchNodeList(key); 871 if (nodes.size() > 0) 872 { 873 return fetchChildConfigs(nodes.get(0)); 874 } 875 else 876 { 877 return Collections.emptyList(); 878 } 879 } 880 881 /** 882 * Finds the override configurations that are defined as top level elements 883 * in the configuration definition file. This method will fetch the child 884 * elements of the root node and remove the nodes that represent other 885 * configuration sections. The remaining nodes are treated as definitions 886 * for override configurations. 887 * 888 * @return a list with subnode configurations for the top level override 889 * configurations 890 */ 891 private List<SubnodeConfiguration> fetchTopLevelOverrideConfigs() 892 { 893 List<SubnodeConfiguration> configs = fetchChildConfigs(getRootNode()); 894 for (Iterator<SubnodeConfiguration> it = configs.iterator(); it.hasNext();) 895 { 896 String nodeName = it.next().getRootNode().getName(); 897 for (int i = 0; i < CONFIG_SECTIONS.length; i++) 898 { 899 if (CONFIG_SECTIONS[i].equals(nodeName)) 900 { 901 it.remove(); 902 break; 903 } 904 } 905 } 906 return configs; 907 } 908 909 /** 910 * Registers the bean factory used by this class if necessary. This method 911 * is called by the constructor to ensure that the required bean factory is 912 * available. 913 */ 914 private void registerBeanFactory() 915 { 916 synchronized (DefaultConfigurationBuilder.class) 917 { 918 if (!BeanHelper.registeredFactoryNames().contains( 919 CONFIG_BEAN_FACTORY_NAME)) 920 { 921 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME, 922 new ConfigurationBeanFactory()); 923 } 924 } 925 } 926 927 /** 928 * <p> 929 * A base class for creating and initializing configuration sources. 930 * </p> 931 * <p> 932 * Concrete sub classes of this base class are responsible for creating 933 * specific {@code Configuration} objects for the tags in the 934 * configuration definition file. The configuration factory will parse the 935 * definition file and try to find a matching 936 * {@code ConfigurationProvider} for each encountered tag. This 937 * provider is then asked to create a corresponding 938 * {@code Configuration} object. It is up to a concrete 939 * implementation how this object is created and initialized. 940 * </p> 941 * <p> 942 * Note that at the moment only configuration classes derived from 943 * {@link AbstractConfiguration} are supported. 944 * </p> 945 */ 946 public static class ConfigurationProvider extends DefaultBeanFactory 947 { 948 /** Stores the class of the configuration to be created. */ 949 private Class<?> configurationClass; 950 951 /** Stores the name of the configuration class to be created.*/ 952 private String configurationClassName; 953 954 /** 955 * Creates a new uninitialized instance of {@code ConfigurationProvider}. 956 */ 957 public ConfigurationProvider() 958 { 959 this((Class<?>) null); 960 } 961 962 /** 963 * Creates a new instance of {@code ConfigurationProvider} and 964 * sets the class of the configuration created by this provider. 965 * 966 * @param configClass the configuration class 967 */ 968 public ConfigurationProvider(Class<?> configClass) 969 { 970 setConfigurationClass(configClass); 971 } 972 973 /** 974 * Creates a new instance of {@code ConfigurationProvider} and 975 * sets the name of the class of the configuration created by this 976 * provider. 977 * 978 * @param configClassName the name of the configuration class 979 * @since 1.4 980 */ 981 public ConfigurationProvider(String configClassName) 982 { 983 setConfigurationClassName(configClassName); 984 } 985 986 /** 987 * Returns the class of the configuration returned by this provider. 988 * 989 * @return the class of the provided configuration 990 */ 991 public Class<?> getConfigurationClass() 992 { 993 return configurationClass; 994 } 995 996 /** 997 * Sets the class of the configuration returned by this provider. 998 * 999 * @param configurationClass the configuration class 1000 */ 1001 public void setConfigurationClass(Class<?> configurationClass) 1002 { 1003 this.configurationClass = configurationClass; 1004 } 1005 1006 /** 1007 * Returns the name of the configuration class returned by this 1008 * provider. 1009 * 1010 * @return the configuration class name 1011 * @since 1.4 1012 */ 1013 public String getConfigurationClassName() 1014 { 1015 return configurationClassName; 1016 } 1017 1018 /** 1019 * Sets the name of the configuration class returned by this provider. 1020 * 1021 * @param configurationClassName the name of the configuration class 1022 * @since 1.4 1023 */ 1024 public void setConfigurationClassName(String configurationClassName) 1025 { 1026 this.configurationClassName = configurationClassName; 1027 } 1028 1029 /** 1030 * Returns the configuration. This method is called to fetch the 1031 * configuration from the provider. This implementation will call the 1032 * inherited {@link 1033 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object) 1034 * createBean()} method to create a new instance of the 1035 * configuration class. 1036 * 1037 * @param decl the bean declaration with initialization parameters for 1038 * the configuration 1039 * @return the new configuration object 1040 * @throws Exception if an error occurs 1041 */ 1042 public AbstractConfiguration getConfiguration( 1043 ConfigurationDeclaration decl) throws Exception 1044 { 1045 return (AbstractConfiguration) createBean(fetchConfigurationClass(), 1046 decl, null); 1047 } 1048 1049 /** 1050 * Returns an uninitialized configuration of the represented type. This 1051 * method will be called for optional configurations when the 1052 * {@code getConfiguration()} method caused an error and the 1053 * {@code forceCreate} attribute is set. A concrete sub class can 1054 * here try to create an uninitialized, empty configuration, which may 1055 * be possible if the error was created during initialization. This base 1056 * implementation just returns <b>null</b>. 1057 * 1058 * @param decl the bean declaration with initialization parameters for 1059 * the configuration 1060 * @return the new configuration object 1061 * @throws Exception if an error occurs 1062 * @since 1.4 1063 */ 1064 public AbstractConfiguration getEmptyConfiguration( 1065 ConfigurationDeclaration decl) throws Exception 1066 { 1067 return null; 1068 } 1069 1070 /** 1071 * Returns the configuration class supported by this provider. If a 1072 * class object was set, it is returned. Otherwise the method tries to 1073 * resolve the class name. 1074 * 1075 * @return the class of the configuration to be created 1076 * @since 1.4 1077 */ 1078 protected synchronized Class<?> fetchConfigurationClass() throws Exception 1079 { 1080 if (getConfigurationClass() == null) 1081 { 1082 setConfigurationClass(loadClass(getConfigurationClassName())); 1083 } 1084 return getConfigurationClass(); 1085 } 1086 1087 /** 1088 * Loads the class with the specified name dynamically. If the class's 1089 * name is <b>null</b>, <b>null</b> will also be returned. 1090 * 1091 * @param className the name of the class to be loaded 1092 * @return the class object 1093 * @throws ClassNotFoundException if class loading fails 1094 * @since 1.4 1095 */ 1096 protected Class<?> loadClass(String className) 1097 throws ClassNotFoundException 1098 { 1099 return (className != null) ? Class.forName(className, true, 1100 getClass().getClassLoader()) : null; 1101 } 1102 } 1103 1104 /** 1105 * <p> 1106 * A specialized {@code BeanDeclaration} implementation that 1107 * represents the declaration of a configuration source. 1108 * </p> 1109 * <p> 1110 * Instances of this class are able to extract all information about a 1111 * configuration source from the configuration definition file. The 1112 * declaration of a configuration source is very similar to a bean 1113 * declaration processed by {@code XMLBeanDeclaration}. There are 1114 * very few differences, e.g. some reserved attributes like 1115 * {@code optional} and {@code at} and the fact that a bean 1116 * factory is never needed. 1117 * </p> 1118 */ 1119 public static class ConfigurationDeclaration extends XMLBeanDeclaration 1120 { 1121 /** Stores a reference to the associated configuration builder. */ 1122 private DefaultConfigurationBuilder configurationBuilder; 1123 1124 /** 1125 * Creates a new instance of {@code ConfigurationDeclaration} and 1126 * initializes it. 1127 * 1128 * @param builder the associated configuration builder 1129 * @param config the configuration this declaration is based onto 1130 */ 1131 public ConfigurationDeclaration(DefaultConfigurationBuilder builder, 1132 HierarchicalConfiguration config) 1133 { 1134 super(config); 1135 configurationBuilder = builder; 1136 } 1137 1138 /** 1139 * Returns the associated configuration builder. 1140 * 1141 * @return the configuration builder 1142 */ 1143 public DefaultConfigurationBuilder getConfigurationBuilder() 1144 { 1145 return configurationBuilder; 1146 } 1147 1148 /** 1149 * Returns the value of the {@code at} attribute. 1150 * 1151 * @return the value of the {@code at} attribute (can be <b>null</b>) 1152 */ 1153 public String getAt() 1154 { 1155 String result = this.getConfiguration().getString(ATTR_AT_RES); 1156 return (result == null) ? this.getConfiguration().getString(ATTR_AT) 1157 : result; 1158 } 1159 1160 /** 1161 * Returns a flag whether this is an optional configuration. 1162 * 1163 * @return a flag if this declaration points to an optional 1164 * configuration 1165 */ 1166 public boolean isOptional() 1167 { 1168 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES, 1169 null); 1170 if (value == null) 1171 { 1172 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL, 1173 Boolean.FALSE); 1174 } 1175 return value.booleanValue(); 1176 } 1177 1178 /** 1179 * Returns a flag whether this configuration should always be created 1180 * and added to the resulting combined configuration. This flag is 1181 * evaluated only for optional configurations whose normal creation has 1182 * caused an error. If for such a configuration the 1183 * {@code forceCreate} attribute is set and the corresponding 1184 * configuration provider supports this mode, an empty configuration 1185 * will be created and added to the resulting combined configuration. 1186 * 1187 * @return the value of the {@code forceCreate} attribute 1188 * @since 1.4 1189 */ 1190 public boolean isForceCreate() 1191 { 1192 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false); 1193 } 1194 1195 /** 1196 * Returns the name of the bean factory. For configuration source 1197 * declarations always a reserved factory is used. This factory's name 1198 * is returned by this implementation. 1199 * 1200 * @return the name of the bean factory 1201 */ 1202 @Override 1203 public String getBeanFactoryName() 1204 { 1205 return CONFIG_BEAN_FACTORY_NAME; 1206 } 1207 1208 /** 1209 * Returns the bean's class name. This implementation will always return 1210 * <b>null</b>. 1211 * 1212 * @return the name of the bean's class 1213 */ 1214 @Override 1215 public String getBeanClassName() 1216 { 1217 return null; 1218 } 1219 1220 /** 1221 * Checks whether the given node is reserved. This method will take 1222 * further reserved attributes into account 1223 * 1224 * @param nd the node 1225 * @return a flag whether this node is reserved 1226 */ 1227 @Override 1228 protected boolean isReservedNode(ConfigurationNode nd) 1229 { 1230 if (super.isReservedNode(nd)) 1231 { 1232 return true; 1233 } 1234 1235 return nd.isAttribute() 1236 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode() 1237 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME 1238 .equals(nd.getName()) && nd.getParentNode() 1239 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0)); 1240 } 1241 1242 /** 1243 * Performs interpolation. This implementation will delegate 1244 * interpolation to the configuration builder, which takes care that the 1245 * currently constructed configuration is taken into account, too. 1246 * 1247 * @param value the value to be interpolated 1248 * @return the interpolated value 1249 */ 1250 @Override 1251 protected Object interpolate(Object value) 1252 { 1253 return getConfigurationBuilder().interpolate(value); 1254 } 1255 } 1256 1257 /** 1258 * A specialized {@code BeanFactory} implementation that handles 1259 * configuration declarations. This class will retrieve the correct 1260 * configuration provider and delegate the task of creating the 1261 * configuration to this object. 1262 */ 1263 static class ConfigurationBeanFactory implements BeanFactory 1264 { 1265 /** The logger. */ 1266 private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class); 1267 1268 /** 1269 * Creates an instance of a bean class. This implementation expects that 1270 * the passed in bean declaration is a declaration for a configuration. 1271 * It will determine the responsible configuration provider and delegate 1272 * the call to this instance. If creation of the configuration fails 1273 * and the {@code optional} attribute is set, the exception will 1274 * be ignored. If the {@code forceCreate} attribute is set, too, 1275 * the provider is asked to create an empty configuration. A return 1276 * value of <b>null</b> means that no configuration could be created. 1277 * 1278 * @param beanClass the bean class (will be ignored) 1279 * @param data the declaration 1280 * @param param an additional parameter (will be ignored) 1281 * @return the newly created configuration 1282 * @throws Exception if an error occurs 1283 */ 1284 public Object createBean(Class<?> beanClass, BeanDeclaration data, 1285 Object param) throws Exception 1286 { 1287 ConfigurationDeclaration decl = (ConfigurationDeclaration) data; 1288 String tagName = decl.getNode().getName(); 1289 ConfigurationProvider provider = decl.getConfigurationBuilder() 1290 .providerForTag(tagName); 1291 if (provider == null) 1292 { 1293 throw new ConfigurationRuntimeException( 1294 "No ConfigurationProvider registered for tag " 1295 + tagName); 1296 } 1297 1298 try 1299 { 1300 return provider.getConfiguration(decl); 1301 } 1302 catch (Exception ex) 1303 { 1304 // If this is an optional configuration, ignore the exception 1305 if (!decl.isOptional()) 1306 { 1307 throw ex; 1308 } 1309 else 1310 { 1311 if (logger.isDebugEnabled()) 1312 { 1313 logger.debug("Load failed for optional configuration " + tagName + ": " 1314 + ex.getMessage()); 1315 } 1316 // Notify registered error listeners 1317 decl.getConfigurationBuilder().fireError( 1318 EVENT_ERR_LOAD_OPTIONAL, 1319 decl.getConfiguration().getString(ATTR_NAME), null, 1320 ex); 1321 1322 if (decl.isForceCreate()) 1323 { 1324 try 1325 { 1326 return provider.getEmptyConfiguration(decl); 1327 } 1328 catch (Exception ex2) 1329 { 1330 // Ignore exception, return null in this case 1331 ; 1332 } 1333 } 1334 return null; 1335 } 1336 } 1337 } 1338 1339 /** 1340 * Returns the default class for this bean factory. 1341 * 1342 * @return the default class 1343 */ 1344 public Class<?> getDefaultBeanClass() 1345 { 1346 // Here some valid class must be returned, otherwise BeanHelper 1347 // will complain that the bean's class cannot be determined 1348 return Configuration.class; 1349 } 1350 } 1351 1352 /** 1353 * A specialized provider implementation that deals with file based 1354 * configurations. Ensures that the base path is correctly set and that the 1355 * load() method gets called. 1356 */ 1357 public static class FileConfigurationProvider extends ConfigurationProvider 1358 { 1359 /** 1360 * Creates a new instance of {@code FileConfigurationProvider}. 1361 */ 1362 public FileConfigurationProvider() 1363 { 1364 super(); 1365 } 1366 1367 /** 1368 * Creates a new instance of {@code FileConfigurationProvider} 1369 * and sets the configuration class. 1370 * 1371 * @param configClass the class for the configurations to be created 1372 */ 1373 public FileConfigurationProvider(Class<?> configClass) 1374 { 1375 super(configClass); 1376 } 1377 1378 /** 1379 * Creates a new instance of {@code FileConfigurationProvider} 1380 * and sets the configuration class name. 1381 * 1382 * @param configClassName the name of the configuration to be created 1383 * @since 1.4 1384 */ 1385 public FileConfigurationProvider(String configClassName) 1386 { 1387 super(configClassName); 1388 } 1389 1390 /** 1391 * Creates the configuration. After that {@code load()} will be 1392 * called. If this configuration is marked as optional, exceptions will 1393 * be ignored. 1394 * 1395 * @param decl the declaration 1396 * @return the new configuration 1397 * @throws Exception if an error occurs 1398 */ 1399 @Override 1400 public AbstractConfiguration getConfiguration( 1401 ConfigurationDeclaration decl) throws Exception 1402 { 1403 AbstractConfiguration result = getEmptyConfiguration(decl); 1404 if (result instanceof FileSystemBased) 1405 { 1406 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder(); 1407 if (builder.getFileSystem() != null) 1408 { 1409 ((FileSystemBased) result).setFileSystem(builder.getFileSystem()); 1410 } 1411 } 1412 ((FileConfiguration) result).load(); 1413 return result; 1414 } 1415 1416 /** 1417 * Returns an uninitialized file configuration. This method will be 1418 * called for optional configurations when the 1419 * {@code getConfiguration()} method caused an error and the 1420 * {@code forceCreate} attribute is set. It will create the 1421 * configuration of the represented type, but the {@code load()} 1422 * method won't be called. This way non-existing configuration files can 1423 * be handled gracefully: If loading a the file fails, an empty 1424 * configuration will be created that is already configured with the 1425 * correct file name. 1426 * 1427 * @param decl the bean declaration with initialization parameters for 1428 * the configuration 1429 * @return the new configuration object 1430 * @throws Exception if an error occurs 1431 * @since 1.4 1432 */ 1433 @Override 1434 public AbstractConfiguration getEmptyConfiguration( 1435 ConfigurationDeclaration decl) throws Exception 1436 { 1437 AbstractConfiguration config = super.getConfiguration(decl); 1438 1439 /** 1440 * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations 1441 * they construct buy may not be an XMLConfiguration. 1442 */ 1443 if (config instanceof EntityResolverSupport) 1444 { 1445 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder(); 1446 EntityResolver resolver = builder.getEntityResolver(); 1447 ((EntityResolverSupport) config).setEntityResolver(resolver); 1448 } 1449 1450 return config; 1451 } 1452 1453 /** 1454 * Initializes the bean instance. Ensures that the file configuration's 1455 * base path will be initialized with the base path of the factory so 1456 * that relative path names can be correctly resolved. 1457 * 1458 * @param bean the bean to be initialized 1459 * @param data the declaration 1460 * @throws Exception if an error occurs 1461 */ 1462 @Override 1463 protected void initBeanInstance(Object bean, BeanDeclaration data) 1464 throws Exception 1465 { 1466 FileConfiguration config = (FileConfiguration) bean; 1467 config.setBasePath(((ConfigurationDeclaration) data) 1468 .getConfigurationBuilder().getConfigurationBasePath()); 1469 super.initBeanInstance(bean, data); 1470 } 1471 } 1472 1473 /** 1474 * A specialized configuration provider for XML configurations. This 1475 * implementation acts like a {@code FileConfigurationProvider}, but 1476 * it will copy all entity IDs that have been registered for the 1477 * configuration builder to the new XML configuration before it is loaded. 1478 * 1479 * @since 1.6 1480 */ 1481 public static class XMLConfigurationProvider extends FileConfigurationProvider 1482 { 1483 /** 1484 * Creates a new instance of {@code XMLConfigurationProvider}. 1485 */ 1486 public XMLConfigurationProvider() 1487 { 1488 super(XMLConfiguration.class); 1489 } 1490 1491 /** 1492 * Returns a new empty configuration instance. This implementation 1493 * performs some additional initialization specific to XML 1494 * configurations. 1495 * 1496 * @param decl the configuration declaration 1497 * @return the new configuration 1498 * @throws Exception if an error occurs 1499 */ 1500 @Override 1501 public AbstractConfiguration getEmptyConfiguration( 1502 ConfigurationDeclaration decl) throws Exception 1503 { 1504 XMLConfiguration config = (XMLConfiguration) super 1505 .getEmptyConfiguration(decl); 1506 1507 DefaultConfigurationBuilder builder = decl 1508 .getConfigurationBuilder(); 1509 EntityResolver resolver = builder.getEntityResolver(); 1510 if (resolver instanceof EntityRegistry) 1511 { 1512 // copy the registered entities 1513 config.getRegisteredEntities().putAll( 1514 builder.getRegisteredEntities()); 1515 } 1516 else 1517 { 1518 config.setEntityResolver(resolver); 1519 } 1520 return config; 1521 } 1522 } 1523 1524 /** 1525 * A specialized configuration provider for file based configurations that 1526 * can handle configuration sources whose concrete type depends on the 1527 * extension of the file to be loaded. One example is the 1528 * {@code properties} tag: if the file ends with ".xml" a 1529 * XMLPropertiesConfiguration object must be created, otherwise a 1530 * PropertiesConfiguration object. 1531 */ 1532 static class FileExtensionConfigurationProvider extends 1533 FileConfigurationProvider 1534 { 1535 /** 1536 * Stores the class to be created when the file extension matches. 1537 */ 1538 private Class<?> matchingClass; 1539 1540 /** 1541 * Stores the name of the class to be created when the file extension 1542 * matches. 1543 */ 1544 private String matchingClassName; 1545 1546 /** 1547 * Stores the class to be created when the file extension does not 1548 * match. 1549 */ 1550 private Class<?> defaultClass; 1551 1552 /** 1553 * Stores the name of the class to be created when the file extension 1554 * does not match. 1555 */ 1556 private String defaultClassName; 1557 1558 /** Stores the file extension to be checked against. */ 1559 private String fileExtension; 1560 1561 /** 1562 * Creates a new instance of 1563 * {@code FileExtensionConfigurationProvider} and initializes it. 1564 * 1565 * @param matchingClass the class to be created when the file extension 1566 * matches 1567 * @param defaultClass the class to be created when the file extension 1568 * does not match 1569 * @param extension the file extension to be checked against 1570 */ 1571 public FileExtensionConfigurationProvider(Class<?> matchingClass, 1572 Class<?> defaultClass, String extension) 1573 { 1574 this.matchingClass = matchingClass; 1575 this.defaultClass = defaultClass; 1576 fileExtension = extension; 1577 } 1578 1579 /** 1580 * Creates a new instance of 1581 * {@code FileExtensionConfigurationProvider} and initializes it 1582 * with the names of the classes to be created. 1583 * 1584 * @param matchingClassName the name of the class to be created when the 1585 * file extension matches 1586 * @param defaultClassName the name of the class to be created when the 1587 * file extension does not match 1588 * @param extension the file extension to be checked against 1589 * @since 1.4 1590 */ 1591 public FileExtensionConfigurationProvider(String matchingClassName, 1592 String defaultClassName, String extension) 1593 { 1594 this.matchingClassName = matchingClassName; 1595 this.defaultClassName = defaultClassName; 1596 fileExtension = extension; 1597 } 1598 1599 /** 1600 * Returns the matching class object, no matter whether it was defined 1601 * as a class or as a class name. 1602 * 1603 * @return the matching class object 1604 * @throws Exception if an error occurs 1605 * @since 1.4 1606 */ 1607 protected synchronized Class<?> fetchMatchingClass() throws Exception 1608 { 1609 if (matchingClass == null) 1610 { 1611 matchingClass = loadClass(matchingClassName); 1612 } 1613 return matchingClass; 1614 } 1615 1616 /** 1617 * Returns the default class object, no matter whether it was defined as 1618 * a class or as a class name. 1619 * 1620 * @return the default class object 1621 * @throws Exception if an error occurs 1622 * @since 1.4 1623 */ 1624 protected synchronized Class<?> fetchDefaultClass() throws Exception 1625 { 1626 if (defaultClass == null) 1627 { 1628 defaultClass = loadClass(defaultClassName); 1629 } 1630 return defaultClass; 1631 } 1632 1633 /** 1634 * Creates the configuration object. The class is determined by the file 1635 * name's extension. 1636 * 1637 * @param beanClass the class 1638 * @param data the bean declaration 1639 * @return the new bean 1640 * @throws Exception if an error occurs 1641 */ 1642 @Override 1643 protected Object createBeanInstance(Class<?> beanClass, 1644 BeanDeclaration data) throws Exception 1645 { 1646 String fileName = ((ConfigurationDeclaration) data) 1647 .getConfiguration().getString(ATTR_FILENAME); 1648 if (fileName != null 1649 && fileName.toLowerCase().trim().endsWith(fileExtension)) 1650 { 1651 return super.createBeanInstance(fetchMatchingClass(), data); 1652 } 1653 else 1654 { 1655 return super.createBeanInstance(fetchDefaultClass(), data); 1656 } 1657 } 1658 } 1659 1660 /** 1661 * A specialized configuration provider class that allows to include other 1662 * configuration definition files. 1663 */ 1664 static class ConfigurationBuilderProvider extends ConfigurationProvider 1665 { 1666 /** 1667 * Creates a new instance of {@code ConfigurationBuilderProvider}. 1668 */ 1669 public ConfigurationBuilderProvider() 1670 { 1671 super(DefaultConfigurationBuilder.class); 1672 } 1673 1674 /** 1675 * Creates the configuration. First creates a configuration builder 1676 * object. Then returns the configuration created by this builder. 1677 * 1678 * @param decl the configuration declaration 1679 * @return the configuration 1680 * @exception Exception if an error occurs 1681 */ 1682 @Override 1683 public AbstractConfiguration getConfiguration( 1684 ConfigurationDeclaration decl) throws Exception 1685 { 1686 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super 1687 .getConfiguration(decl); 1688 return builder.getConfiguration(true); 1689 } 1690 1691 /** 1692 * Returns an empty configuration in case of an optional configuration 1693 * could not be created. This implementation returns an empty combined 1694 * configuration. 1695 * 1696 * @param decl the configuration declaration 1697 * @return the configuration 1698 * @exception Exception if an error occurs 1699 * @since 1.4 1700 */ 1701 @Override 1702 public AbstractConfiguration getEmptyConfiguration( 1703 ConfigurationDeclaration decl) throws Exception 1704 { 1705 return new CombinedConfiguration(); 1706 } 1707 1708 /** 1709 * {@inheritDoc} This implementation ensures that the configuration 1710 * builder created by this provider inherits the properties from the 1711 * current configuration builder. 1712 */ 1713 @Override 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 (ConfigurationListener l : parent.getConfigurationListeners()) 1747 { 1748 child.addConfigurationListener(l); 1749 } 1750 child.clearErrorListeners(); 1751 for (ConfigurationErrorListener l : parent.getErrorListeners()) 1752 { 1753 child.addErrorListener(l); 1754 } 1755 } 1756 } 1757 }