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 */ 017package org.apache.commons.configuration; 018 019import java.io.File; 020import java.net.URL; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.configuration.beanutils.BeanDeclaration; 029import org.apache.commons.configuration.beanutils.BeanFactory; 030import org.apache.commons.configuration.beanutils.BeanHelper; 031import org.apache.commons.configuration.beanutils.DefaultBeanFactory; 032import org.apache.commons.configuration.beanutils.XMLBeanDeclaration; 033import org.apache.commons.configuration.event.ConfigurationErrorListener; 034import org.apache.commons.configuration.event.ConfigurationListener; 035import org.apache.commons.configuration.interpol.ConfigurationInterpolator; 036import org.apache.commons.configuration.resolver.CatalogResolver; 037import org.apache.commons.configuration.resolver.EntityRegistry; 038import org.apache.commons.configuration.resolver.EntityResolverSupport; 039import org.apache.commons.configuration.tree.ConfigurationNode; 040import org.apache.commons.configuration.tree.DefaultExpressionEngine; 041import org.apache.commons.configuration.tree.OverrideCombiner; 042import org.apache.commons.configuration.tree.UnionCombiner; 043import org.apache.commons.lang.text.StrLookup; 044import org.apache.commons.logging.Log; 045import org.apache.commons.logging.LogFactory; 046import 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 1366930 2012-07-29 20:05:36Z oheger $ 218 */ 219public 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 /** 418 * A specialized {@code StrLookup} object which operates on the combined 419 * configuration constructed by this builder. This object is used as 420 * default lookup for {@code ConfigurationInterpolator} objects assigned to 421 * newly created configuration objects. 422 */ 423 private final StrLookup combinedConfigLookup = new StrLookup() 424 { 425 @Override 426 public String lookup(String key) 427 { 428 if (constructedConfiguration != null) 429 { 430 Object value = 431 constructedConfiguration.resolveContainerStore(key); 432 return (value != null) ? value.toString() : null; 433 } 434 return null; 435 } 436 }; 437 438 /** Stores the configuration that is currently constructed.*/ 439 private CombinedConfiguration constructedConfiguration; 440 441 /** Stores a map with the registered configuration providers. */ 442 private final Map<String, ConfigurationProvider> providers; 443 444 /** Stores the base path to the configuration sources to load. */ 445 private String configurationBasePath; 446 447 /** 448 * Creates a new instance of {@code DefaultConfigurationBuilder}. A 449 * configuration definition file is not yet loaded. Use the diverse setter 450 * methods provided by file based configurations to specify the 451 * configuration definition file. 452 */ 453 public DefaultConfigurationBuilder() 454 { 455 super(); 456 providers = new HashMap<String, ConfigurationProvider>(); 457 registerDefaultProviders(); 458 registerBeanFactory(); 459 setLogger(LogFactory.getLog(getClass())); 460 addErrorLogListener(); // log errors per default 461 } 462 463 /** 464 * Creates a new instance of {@code DefaultConfigurationBuilder} and 465 * sets the specified configuration definition file. 466 * 467 * @param file the configuration definition file 468 */ 469 public DefaultConfigurationBuilder(File file) 470 { 471 this(); 472 setFile(file); 473 } 474 475 /** 476 * Creates a new instance of {@code DefaultConfigurationBuilder} and 477 * sets the specified configuration definition file. 478 * 479 * @param fileName the name of the configuration definition file 480 * @throws ConfigurationException if an error occurs when the file is loaded 481 */ 482 public DefaultConfigurationBuilder(String fileName) 483 throws ConfigurationException 484 { 485 this(); 486 setFileName(fileName); 487 } 488 489 /** 490 * Creates a new instance of {@code DefaultConfigurationBuilder} and 491 * sets the specified configuration definition file. 492 * 493 * @param url the URL to the configuration definition file 494 * @throws ConfigurationException if an error occurs when the file is loaded 495 */ 496 public DefaultConfigurationBuilder(URL url) throws ConfigurationException 497 { 498 this(); 499 setURL(url); 500 } 501 502 /** 503 * Returns the base path for the configuration sources to load. This path is 504 * used to resolve relative paths in the configuration definition file. 505 * 506 * @return the base path for configuration sources 507 */ 508 public String getConfigurationBasePath() 509 { 510 return (configurationBasePath != null) ? configurationBasePath 511 : getBasePath(); 512 } 513 514 /** 515 * Sets the base path for the configuration sources to load. Normally a base 516 * path need not to be set because it is determined by the location of the 517 * configuration definition file to load. All relative paths in this file 518 * are resolved relative to this file. Setting a base path makes sense if 519 * such relative paths should be otherwise resolved, e.g. if the 520 * configuration file is loaded from the class path and all sub 521 * configurations it refers to are stored in a special config directory. 522 * 523 * @param configurationBasePath the new base path to set 524 */ 525 public void setConfigurationBasePath(String configurationBasePath) 526 { 527 this.configurationBasePath = configurationBasePath; 528 } 529 530 /** 531 * Adds a configuration provider for the specified tag. Whenever this tag is 532 * encountered in the configuration definition file this provider will be 533 * called to create the configuration object. 534 * 535 * @param tagName the name of the tag in the configuration definition file 536 * @param provider the provider for this tag 537 */ 538 public void addConfigurationProvider(String tagName, 539 ConfigurationProvider provider) 540 { 541 if (tagName == null) 542 { 543 throw new IllegalArgumentException("Tag name must not be null!"); 544 } 545 if (provider == null) 546 { 547 throw new IllegalArgumentException("Provider must not be null!"); 548 } 549 550 providers.put(tagName, provider); 551 } 552 553 /** 554 * Removes the configuration provider for the specified tag name. 555 * 556 * @param tagName the tag name 557 * @return the removed configuration provider or <b>null</b> if none was 558 * registered for that tag 559 */ 560 public ConfigurationProvider removeConfigurationProvider(String tagName) 561 { 562 return providers.remove(tagName); 563 } 564 565 /** 566 * Returns the configuration provider for the given tag. 567 * 568 * @param tagName the name of the tag 569 * @return the provider that was registered for this tag or <b>null</b> if 570 * there is none 571 */ 572 public ConfigurationProvider providerForTag(String tagName) 573 { 574 return providers.get(tagName); 575 } 576 577 /** 578 * Returns the configuration provided by this builder. Loads and parses the 579 * configuration definition file and creates instances for the declared 580 * configurations. 581 * 582 * @return the configuration 583 * @throws ConfigurationException if an error occurs 584 */ 585 public Configuration getConfiguration() throws ConfigurationException 586 { 587 return getConfiguration(true); 588 } 589 590 /** 591 * Returns the configuration provided by this builder. If the boolean 592 * parameter is <b>true</b>, the configuration definition file will be 593 * loaded. It will then be parsed, and instances for the declared 594 * configurations will be created. 595 * 596 * @param load a flag whether the configuration definition file should be 597 * loaded; a value of <b>false</b> would make sense if the file has already 598 * been created or its content was manipulated using some of the property 599 * accessor methods 600 * @return the configuration 601 * @throws ConfigurationException if an error occurs 602 */ 603 public CombinedConfiguration getConfiguration(boolean load) 604 throws ConfigurationException 605 { 606 if (load) 607 { 608 load(); 609 } 610 611 initFileSystem(); 612 initSystemProperties(); 613 configureEntityResolver(); 614 registerConfiguredProviders(); 615 registerConfiguredLookups(); 616 617 CombinedConfiguration result = createResultConfiguration(); 618 constructedConfiguration = result; 619 620 List<SubnodeConfiguration> overrides = fetchTopLevelOverrideConfigs(); 621 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE)); 622 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST); 623 624 List<SubnodeConfiguration> additionals = fetchChildConfigs(KEY_UNION); 625 if (!additionals.isEmpty()) 626 { 627 CombinedConfiguration addConfig = createAdditionalsConfiguration(result); 628 result.addConfiguration(addConfig, ADDITIONAL_NAME); 629 initCombinedConfiguration(addConfig, additionals, 630 KEY_ADDITIONAL_LIST); 631 } 632 633 return result; 634 } 635 636 /** 637 * Creates the resulting combined configuration. This method is called by 638 * {@code getConfiguration()}. It checks whether the 639 * {@code header} section of the configuration definition file 640 * contains a {@code result} element. If this is the case, it will be 641 * used to initialize the properties of the newly created configuration 642 * object. 643 * 644 * @return the resulting configuration object 645 * @throws ConfigurationException if an error occurs 646 */ 647 protected CombinedConfiguration createResultConfiguration() 648 throws ConfigurationException 649 { 650 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true); 651 CombinedConfiguration result = (CombinedConfiguration) BeanHelper 652 .createBean(decl, CombinedConfiguration.class); 653 654 if (getMaxIndex(KEY_COMBINER) < 0) 655 { 656 // No combiner defined => set default 657 result.setNodeCombiner(new OverrideCombiner()); 658 } 659 660 return result; 661 } 662 663 /** 664 * Creates the {@code CombinedConfiguration} for the configuration 665 * sources in the <code><additional></code> section. This method is 666 * called when the builder constructs the final configuration. It creates a 667 * new {@code CombinedConfiguration} and initializes some properties 668 * from the result configuration. 669 * 670 * @param resultConfig the result configuration (this is the configuration 671 * that will be returned by the builder) 672 * @return the {@code CombinedConfiguration} for the additional 673 * configuration sources 674 * @since 1.7 675 */ 676 protected CombinedConfiguration createAdditionalsConfiguration( 677 CombinedConfiguration resultConfig) 678 { 679 CombinedConfiguration addConfig = 680 new CombinedConfiguration(new UnionCombiner()); 681 addConfig.setDelimiterParsingDisabled(resultConfig 682 .isDelimiterParsingDisabled()); 683 addConfig.setForceReloadCheck(resultConfig.isForceReloadCheck()); 684 addConfig.setIgnoreReloadExceptions(resultConfig 685 .isIgnoreReloadExceptions()); 686 return addConfig; 687 } 688 689 /** 690 * Initializes a combined configuration for the configurations of a specific 691 * section. This method is called for the override and for the additional 692 * section (if it exists). 693 * 694 * @param config the configuration to be initialized 695 * @param containedConfigs the list with the declarations of the contained 696 * configurations 697 * @param keyListNodes a list with the declaration of list nodes 698 * @throws ConfigurationException if an error occurs 699 */ 700 protected void initCombinedConfiguration(CombinedConfiguration config, 701 List<? extends HierarchicalConfiguration> containedConfigs, 702 String keyListNodes) throws ConfigurationException 703 { 704 List<Object> listNodes = getList(keyListNodes); 705 for (Object listNode : listNodes) 706 { 707 config.getNodeCombiner().addListNode((String) listNode); 708 } 709 710 for (HierarchicalConfiguration conf : containedConfigs) 711 { 712 ConfigurationDeclaration decl = new ConfigurationDeclaration(this, 713 conf); 714 if (getLogger().isDebugEnabled()) 715 { 716 getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name " 717 + decl.getConfiguration().getString(ATTR_NAME)); 718 } 719 AbstractConfiguration newConf = createConfigurationAt(decl); 720 if (newConf != null) 721 { 722 config.addConfiguration(newConf, decl.getConfiguration() 723 .getString(ATTR_NAME), decl.getAt()); 724 } 725 } 726 } 727 728 /** 729 * Registers the default configuration providers supported by this class. 730 * This method will be called during initialization. It registers 731 * configuration providers for the tags that are supported by default. 732 */ 733 protected void registerDefaultProviders() 734 { 735 for (int i = 0; i < DEFAULT_TAGS.length; i++) 736 { 737 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]); 738 } 739 } 740 741 /** 742 * Registers providers defined in the configuration. 743 * 744 * @throws ConfigurationException if an error occurs 745 */ 746 protected void registerConfiguredProviders() throws ConfigurationException 747 { 748 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS); 749 for (HierarchicalConfiguration config : nodes) 750 { 751 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 752 String key = config.getString(KEY_PROVIDER_KEY); 753 addConfigurationProvider(key, (ConfigurationProvider) BeanHelper 754 .createBean(decl)); 755 } 756 } 757 758 /** 759 * Registers StrLookups defined in the configuration. 760 * 761 * @throws ConfigurationException if an error occurs 762 */ 763 protected void registerConfiguredLookups() throws ConfigurationException 764 { 765 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS); 766 for (HierarchicalConfiguration config : nodes) 767 { 768 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 769 String key = config.getString(KEY_LOOKUP_KEY); 770 StrLookup lookup = (StrLookup) BeanHelper.createBean(decl); 771 BeanHelper.setProperty(lookup, "configuration", this); 772 ConfigurationInterpolator.registerGlobalLookup(key, lookup); 773 this.getInterpolator().registerLookup(key, lookup); 774 } 775 } 776 777 protected void initFileSystem() throws ConfigurationException 778 { 779 if (getMaxIndex(FILE_SYSTEM) == 0) 780 { 781 HierarchicalConfiguration config = configurationAt(FILE_SYSTEM); 782 XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 783 setFileSystem((FileSystem) BeanHelper.createBean(decl)); 784 } 785 } 786 787 /** 788 * If a property file is configured add the properties to the System properties. 789 * @throws ConfigurationException if an error occurs. 790 */ 791 protected void initSystemProperties() throws ConfigurationException 792 { 793 String fileName = getString(KEY_SYSTEM_PROPS); 794 if (fileName != null) 795 { 796 try 797 { 798 SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName); 799 } 800 catch (Exception ex) 801 { 802 throw new ConfigurationException("Error setting system properties from " + fileName, ex); 803 } 804 805 } 806 } 807 808 protected void configureEntityResolver() throws ConfigurationException 809 { 810 if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0) 811 { 812 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true); 813 EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class); 814 BeanHelper.setProperty(resolver, "fileSystem", getFileSystem()); 815 BeanHelper.setProperty(resolver, "baseDir", getBasePath()); 816 BeanHelper.setProperty(resolver, "substitutor", getSubstitutor()); 817 setEntityResolver(resolver); 818 } 819 } 820 821 /** 822 * Performs interpolation. This method will not only take this configuration 823 * instance into account (which is the one that loaded the configuration 824 * definition file), but also the so far constructed combined configuration. 825 * So variables can be used that point to properties that are defined in 826 * configuration sources loaded by this builder. 827 * 828 * @param value the value to be interpolated 829 * @return the interpolated value 830 */ 831 @Override 832 protected Object interpolate(Object value) 833 { 834 Object result = super.interpolate(value); 835 if (constructedConfiguration != null) 836 { 837 result = constructedConfiguration.interpolate(result); 838 } 839 return result; 840 } 841 842 /** 843 * Creates a configuration object from the specified configuration 844 * declaration. 845 * 846 * @param decl the configuration declaration 847 * @return the new configuration object 848 * @throws ConfigurationException if an error occurs 849 */ 850 private AbstractConfiguration createConfigurationAt( 851 ConfigurationDeclaration decl) throws ConfigurationException 852 { 853 try 854 { 855 return (AbstractConfiguration) BeanHelper.createBean(decl); 856 } 857 catch (Exception ex) 858 { 859 // redirect to configuration exceptions 860 throw new ConfigurationException(ex); 861 } 862 } 863 864 /** 865 * Returns a list with {@code SubnodeConfiguration} objects for the 866 * child nodes of the specified configuration node. 867 * 868 * @param node the start node 869 * @return a list with subnode configurations for the node's children 870 */ 871 private List<SubnodeConfiguration> fetchChildConfigs(ConfigurationNode node) 872 { 873 List<ConfigurationNode> children = node.getChildren(); 874 List<SubnodeConfiguration> result = new ArrayList<SubnodeConfiguration>(children.size()); 875 for (ConfigurationNode child : children) 876 { 877 result.add(createSubnodeConfiguration(child)); 878 } 879 return result; 880 } 881 882 /** 883 * Returns a list with {@code SubnodeConfiguration} objects for the 884 * child nodes of the node specified by the given key. 885 * 886 * @param key the key (must define exactly one node) 887 * @return a list with subnode configurations for the node's children 888 */ 889 private List<SubnodeConfiguration> fetchChildConfigs(String key) 890 { 891 List<ConfigurationNode> nodes = fetchNodeList(key); 892 if (nodes.size() > 0) 893 { 894 return fetchChildConfigs(nodes.get(0)); 895 } 896 else 897 { 898 return Collections.emptyList(); 899 } 900 } 901 902 /** 903 * Finds the override configurations that are defined as top level elements 904 * in the configuration definition file. This method will fetch the child 905 * elements of the root node and remove the nodes that represent other 906 * configuration sections. The remaining nodes are treated as definitions 907 * for override configurations. 908 * 909 * @return a list with subnode configurations for the top level override 910 * configurations 911 */ 912 private List<SubnodeConfiguration> fetchTopLevelOverrideConfigs() 913 { 914 List<SubnodeConfiguration> configs = fetchChildConfigs(getRootNode()); 915 for (Iterator<SubnodeConfiguration> it = configs.iterator(); it.hasNext();) 916 { 917 String nodeName = it.next().getRootNode().getName(); 918 for (int i = 0; i < CONFIG_SECTIONS.length; i++) 919 { 920 if (CONFIG_SECTIONS[i].equals(nodeName)) 921 { 922 it.remove(); 923 break; 924 } 925 } 926 } 927 return configs; 928 } 929 930 /** 931 * Registers the bean factory used by this class if necessary. This method 932 * is called by the constructor to ensure that the required bean factory is 933 * available. 934 */ 935 private void registerBeanFactory() 936 { 937 synchronized (DefaultConfigurationBuilder.class) 938 { 939 if (!BeanHelper.registeredFactoryNames().contains( 940 CONFIG_BEAN_FACTORY_NAME)) 941 { 942 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME, 943 new ConfigurationBeanFactory()); 944 } 945 } 946 } 947 948 /** 949 * <p> 950 * A base class for creating and initializing configuration sources. 951 * </p> 952 * <p> 953 * Concrete sub classes of this base class are responsible for creating 954 * specific {@code Configuration} objects for the tags in the 955 * configuration definition file. The configuration factory will parse the 956 * definition file and try to find a matching 957 * {@code ConfigurationProvider} for each encountered tag. This 958 * provider is then asked to create a corresponding 959 * {@code Configuration} object. It is up to a concrete 960 * implementation how this object is created and initialized. 961 * </p> 962 * <p> 963 * Note that at the moment only configuration classes derived from 964 * {@link AbstractConfiguration} are supported. 965 * </p> 966 */ 967 public static class ConfigurationProvider extends DefaultBeanFactory 968 { 969 /** Stores the class of the configuration to be created. */ 970 private Class<?> configurationClass; 971 972 /** Stores the name of the configuration class to be created.*/ 973 private String configurationClassName; 974 975 /** 976 * Creates a new uninitialized instance of {@code ConfigurationProvider}. 977 */ 978 public ConfigurationProvider() 979 { 980 this((Class<?>) null); 981 } 982 983 /** 984 * Creates a new instance of {@code ConfigurationProvider} and 985 * sets the class of the configuration created by this provider. 986 * 987 * @param configClass the configuration class 988 */ 989 public ConfigurationProvider(Class<?> configClass) 990 { 991 setConfigurationClass(configClass); 992 } 993 994 /** 995 * Creates a new instance of {@code ConfigurationProvider} and 996 * sets the name of the class of the configuration created by this 997 * provider. 998 * 999 * @param configClassName the name of the configuration class 1000 * @since 1.4 1001 */ 1002 public ConfigurationProvider(String configClassName) 1003 { 1004 setConfigurationClassName(configClassName); 1005 } 1006 1007 /** 1008 * Returns the class of the configuration returned by this provider. 1009 * 1010 * @return the class of the provided configuration 1011 */ 1012 public Class<?> getConfigurationClass() 1013 { 1014 return configurationClass; 1015 } 1016 1017 /** 1018 * Sets the class of the configuration returned by this provider. 1019 * 1020 * @param configurationClass the configuration class 1021 */ 1022 public void setConfigurationClass(Class<?> configurationClass) 1023 { 1024 this.configurationClass = configurationClass; 1025 } 1026 1027 /** 1028 * Returns the name of the configuration class returned by this 1029 * provider. 1030 * 1031 * @return the configuration class name 1032 * @since 1.4 1033 */ 1034 public String getConfigurationClassName() 1035 { 1036 return configurationClassName; 1037 } 1038 1039 /** 1040 * Sets the name of the configuration class returned by this provider. 1041 * 1042 * @param configurationClassName the name of the configuration class 1043 * @since 1.4 1044 */ 1045 public void setConfigurationClassName(String configurationClassName) 1046 { 1047 this.configurationClassName = configurationClassName; 1048 } 1049 1050 /** 1051 * Returns the configuration. This method is called to fetch the 1052 * configuration from the provider. This implementation will call the 1053 * inherited {@link 1054 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object) 1055 * createBean()} method to create a new instance of the 1056 * configuration class. 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 */ 1063 public AbstractConfiguration getConfiguration( 1064 ConfigurationDeclaration decl) throws Exception 1065 { 1066 return (AbstractConfiguration) createBean(fetchConfigurationClass(), 1067 decl, null); 1068 } 1069 1070 /** 1071 * Returns an uninitialized configuration of the represented type. This 1072 * method will be called for optional configurations when the 1073 * {@code getConfiguration()} method caused an error and the 1074 * {@code forceCreate} attribute is set. A concrete sub class can 1075 * here try to create an uninitialized, empty configuration, which may 1076 * be possible if the error was created during initialization. This base 1077 * implementation just returns <b>null</b>. 1078 * 1079 * @param decl the bean declaration with initialization parameters for 1080 * the configuration 1081 * @return the new configuration object 1082 * @throws Exception if an error occurs 1083 * @since 1.4 1084 */ 1085 public AbstractConfiguration getEmptyConfiguration( 1086 ConfigurationDeclaration decl) throws Exception 1087 { 1088 return null; 1089 } 1090 1091 /** 1092 * Returns the configuration class supported by this provider. If a 1093 * class object was set, it is returned. Otherwise the method tries to 1094 * resolve the class name. 1095 * 1096 * @return the class of the configuration to be created 1097 * @since 1.4 1098 */ 1099 protected synchronized Class<?> fetchConfigurationClass() throws Exception 1100 { 1101 if (getConfigurationClass() == null) 1102 { 1103 setConfigurationClass(loadClass(getConfigurationClassName())); 1104 } 1105 return getConfigurationClass(); 1106 } 1107 1108 /** 1109 * Loads the class with the specified name dynamically. If the class's 1110 * name is <b>null</b>, <b>null</b> will also be returned. 1111 * 1112 * @param className the name of the class to be loaded 1113 * @return the class object 1114 * @throws ClassNotFoundException if class loading fails 1115 * @since 1.4 1116 */ 1117 protected Class<?> loadClass(String className) 1118 throws ClassNotFoundException 1119 { 1120 return (className != null) ? Class.forName(className, true, 1121 getClass().getClassLoader()) : null; 1122 } 1123 } 1124 1125 /** 1126 * <p> 1127 * A specialized {@code BeanDeclaration} implementation that 1128 * represents the declaration of a configuration source. 1129 * </p> 1130 * <p> 1131 * Instances of this class are able to extract all information about a 1132 * configuration source from the configuration definition file. The 1133 * declaration of a configuration source is very similar to a bean 1134 * declaration processed by {@code XMLBeanDeclaration}. There are 1135 * very few differences, e.g. some reserved attributes like 1136 * {@code optional} and {@code at} and the fact that a bean 1137 * factory is never needed. 1138 * </p> 1139 */ 1140 public static class ConfigurationDeclaration extends XMLBeanDeclaration 1141 { 1142 /** Stores a reference to the associated configuration builder. */ 1143 private DefaultConfigurationBuilder configurationBuilder; 1144 1145 /** 1146 * Creates a new instance of {@code ConfigurationDeclaration} and 1147 * initializes it. 1148 * 1149 * @param builder the associated configuration builder 1150 * @param config the configuration this declaration is based onto 1151 */ 1152 public ConfigurationDeclaration(DefaultConfigurationBuilder builder, 1153 HierarchicalConfiguration config) 1154 { 1155 super(config); 1156 configurationBuilder = builder; 1157 } 1158 1159 /** 1160 * Returns the associated configuration builder. 1161 * 1162 * @return the configuration builder 1163 */ 1164 public DefaultConfigurationBuilder getConfigurationBuilder() 1165 { 1166 return configurationBuilder; 1167 } 1168 1169 /** 1170 * Returns the value of the {@code at} attribute. 1171 * 1172 * @return the value of the {@code at} attribute (can be <b>null</b>) 1173 */ 1174 public String getAt() 1175 { 1176 String result = this.getConfiguration().getString(ATTR_AT_RES); 1177 return (result == null) ? this.getConfiguration().getString(ATTR_AT) 1178 : result; 1179 } 1180 1181 /** 1182 * Returns a flag whether this is an optional configuration. 1183 * 1184 * @return a flag if this declaration points to an optional 1185 * configuration 1186 */ 1187 public boolean isOptional() 1188 { 1189 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES, 1190 null); 1191 if (value == null) 1192 { 1193 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL, 1194 Boolean.FALSE); 1195 } 1196 return value.booleanValue(); 1197 } 1198 1199 /** 1200 * Returns a flag whether this configuration should always be created 1201 * and added to the resulting combined configuration. This flag is 1202 * evaluated only for optional configurations whose normal creation has 1203 * caused an error. If for such a configuration the 1204 * {@code forceCreate} attribute is set and the corresponding 1205 * configuration provider supports this mode, an empty configuration 1206 * will be created and added to the resulting combined configuration. 1207 * 1208 * @return the value of the {@code forceCreate} attribute 1209 * @since 1.4 1210 */ 1211 public boolean isForceCreate() 1212 { 1213 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false); 1214 } 1215 1216 /** 1217 * Returns the name of the bean factory. For configuration source 1218 * declarations always a reserved factory is used. This factory's name 1219 * is returned by this implementation. 1220 * 1221 * @return the name of the bean factory 1222 */ 1223 @Override 1224 public String getBeanFactoryName() 1225 { 1226 return CONFIG_BEAN_FACTORY_NAME; 1227 } 1228 1229 /** 1230 * Returns the bean's class name. This implementation will always return 1231 * <b>null</b>. 1232 * 1233 * @return the name of the bean's class 1234 */ 1235 @Override 1236 public String getBeanClassName() 1237 { 1238 return null; 1239 } 1240 1241 /** 1242 * Checks whether the given node is reserved. This method will take 1243 * further reserved attributes into account 1244 * 1245 * @param nd the node 1246 * @return a flag whether this node is reserved 1247 */ 1248 @Override 1249 protected boolean isReservedNode(ConfigurationNode nd) 1250 { 1251 if (super.isReservedNode(nd)) 1252 { 1253 return true; 1254 } 1255 1256 return nd.isAttribute() 1257 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode() 1258 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME 1259 .equals(nd.getName()) && nd.getParentNode() 1260 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0)); 1261 } 1262 1263 /** 1264 * Performs interpolation. This implementation will delegate 1265 * interpolation to the configuration builder, which takes care that the 1266 * currently constructed configuration is taken into account, too. 1267 * 1268 * @param value the value to be interpolated 1269 * @return the interpolated value 1270 */ 1271 @Override 1272 protected Object interpolate(Object value) 1273 { 1274 return getConfigurationBuilder().interpolate(value); 1275 } 1276 } 1277 1278 /** 1279 * A specialized {@code BeanFactory} implementation that handles 1280 * configuration declarations. This class will retrieve the correct 1281 * configuration provider and delegate the task of creating the 1282 * configuration to this object. 1283 */ 1284 static class ConfigurationBeanFactory implements BeanFactory 1285 { 1286 /** The logger. */ 1287 private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class); 1288 1289 /** 1290 * Creates an instance of a bean class. This implementation expects that 1291 * the passed in bean declaration is a declaration for a configuration. 1292 * It will determine the responsible configuration provider and delegate 1293 * the call to this instance. If creation of the configuration fails 1294 * and the {@code optional} attribute is set, the exception will 1295 * be ignored. If the {@code forceCreate} attribute is set, too, 1296 * the provider is asked to create an empty configuration. A return 1297 * value of <b>null</b> means that no configuration could be created. 1298 * 1299 * @param beanClass the bean class (will be ignored) 1300 * @param data the declaration 1301 * @param param an additional parameter (will be ignored) 1302 * @return the newly created configuration 1303 * @throws Exception if an error occurs 1304 */ 1305 public Object createBean(Class<?> beanClass, BeanDeclaration data, 1306 Object param) throws Exception 1307 { 1308 ConfigurationDeclaration decl = (ConfigurationDeclaration) data; 1309 String tagName = decl.getNode().getName(); 1310 ConfigurationProvider provider = decl.getConfigurationBuilder() 1311 .providerForTag(tagName); 1312 if (provider == null) 1313 { 1314 throw new ConfigurationRuntimeException( 1315 "No ConfigurationProvider registered for tag " 1316 + tagName); 1317 } 1318 1319 try 1320 { 1321 AbstractConfiguration config = provider.getConfiguration(decl); 1322 installInterpolator(decl, config); 1323 return config; 1324 } 1325 catch (Exception ex) 1326 { 1327 // If this is an optional configuration, ignore the exception 1328 if (!decl.isOptional()) 1329 { 1330 throw ex; 1331 } 1332 else 1333 { 1334 if (logger.isDebugEnabled()) 1335 { 1336 logger.debug("Load failed for optional configuration " + tagName + ": " 1337 + ex.getMessage()); 1338 } 1339 // Notify registered error listeners 1340 decl.getConfigurationBuilder().fireError( 1341 EVENT_ERR_LOAD_OPTIONAL, 1342 decl.getConfiguration().getString(ATTR_NAME), null, 1343 ex); 1344 1345 if (decl.isForceCreate()) 1346 { 1347 try 1348 { 1349 return provider.getEmptyConfiguration(decl); 1350 } 1351 catch (Exception ex2) 1352 { 1353 // Ignore exception, return null in this case 1354 logger.warn("Could not create instance of optional configuration " 1355 + tagName, ex2); 1356 } 1357 } 1358 return null; 1359 } 1360 } 1361 } 1362 1363 /** 1364 * Returns the default class for this bean factory. 1365 * 1366 * @return the default class 1367 */ 1368 public Class<?> getDefaultBeanClass() 1369 { 1370 // Here some valid class must be returned, otherwise BeanHelper 1371 // will complain that the bean's class cannot be determined 1372 return Configuration.class; 1373 } 1374 1375 /** 1376 * Installs a specialized {@code ConfigurationInterpolator} on a newly 1377 * created configuration which also takes the combined configuration 1378 * created by the builder into account. With this 1379 * {@code ConfigurationInterpolator} the interpolation facilities of 1380 * this child configuration are extended to include all other 1381 * configurations created by this builder. 1382 * 1383 * @param decl the {@code ConfigurationDeclaration} 1384 * @param config the newly created configuration instance 1385 */ 1386 private void installInterpolator(ConfigurationDeclaration decl, 1387 AbstractConfiguration config) 1388 { 1389 ConfigurationInterpolator parent = new ConfigurationInterpolator(); 1390 parent.setDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup); 1391 config.getInterpolator().setParentInterpolator(parent); 1392 } 1393 } 1394 1395 /** 1396 * A specialized provider implementation that deals with file based 1397 * configurations. Ensures that the base path is correctly set and that the 1398 * load() method gets called. 1399 */ 1400 public static class FileConfigurationProvider extends ConfigurationProvider 1401 { 1402 /** 1403 * Creates a new instance of {@code FileConfigurationProvider}. 1404 */ 1405 public FileConfigurationProvider() 1406 { 1407 super(); 1408 } 1409 1410 /** 1411 * Creates a new instance of {@code FileConfigurationProvider} 1412 * and sets the configuration class. 1413 * 1414 * @param configClass the class for the configurations to be created 1415 */ 1416 public FileConfigurationProvider(Class<?> configClass) 1417 { 1418 super(configClass); 1419 } 1420 1421 /** 1422 * Creates a new instance of {@code FileConfigurationProvider} 1423 * and sets the configuration class name. 1424 * 1425 * @param configClassName the name of the configuration to be created 1426 * @since 1.4 1427 */ 1428 public FileConfigurationProvider(String configClassName) 1429 { 1430 super(configClassName); 1431 } 1432 1433 /** 1434 * Creates the configuration. After that {@code load()} will be 1435 * called. If this configuration is marked as optional, exceptions will 1436 * be ignored. 1437 * 1438 * @param decl the declaration 1439 * @return the new configuration 1440 * @throws Exception if an error occurs 1441 */ 1442 @Override 1443 public AbstractConfiguration getConfiguration( 1444 ConfigurationDeclaration decl) throws Exception 1445 { 1446 AbstractConfiguration result = getEmptyConfiguration(decl); 1447 if (result instanceof FileSystemBased) 1448 { 1449 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder(); 1450 if (builder.getFileSystem() != null) 1451 { 1452 ((FileSystemBased) result).setFileSystem(builder.getFileSystem()); 1453 } 1454 } 1455 ((FileConfiguration) result).load(); 1456 return result; 1457 } 1458 1459 /** 1460 * Returns an uninitialized file configuration. This method will be 1461 * called for optional configurations when the 1462 * {@code getConfiguration()} method caused an error and the 1463 * {@code forceCreate} attribute is set. It will create the 1464 * configuration of the represented type, but the {@code load()} 1465 * method won't be called. This way non-existing configuration files can 1466 * be handled gracefully: If loading a the file fails, an empty 1467 * configuration will be created that is already configured with the 1468 * correct file name. 1469 * 1470 * @param decl the bean declaration with initialization parameters for 1471 * the configuration 1472 * @return the new configuration object 1473 * @throws Exception if an error occurs 1474 * @since 1.4 1475 */ 1476 @Override 1477 public AbstractConfiguration getEmptyConfiguration( 1478 ConfigurationDeclaration decl) throws Exception 1479 { 1480 AbstractConfiguration config = super.getConfiguration(decl); 1481 1482 /** 1483 * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations 1484 * they construct buy may not be an XMLConfiguration. 1485 */ 1486 if (config instanceof EntityResolverSupport) 1487 { 1488 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder(); 1489 EntityResolver resolver = builder.getEntityResolver(); 1490 ((EntityResolverSupport) config).setEntityResolver(resolver); 1491 } 1492 1493 return config; 1494 } 1495 1496 /** 1497 * Initializes the bean instance. Ensures that the file configuration's 1498 * base path will be initialized with the base path of the factory so 1499 * that relative path names can be correctly resolved. 1500 * 1501 * @param bean the bean to be initialized 1502 * @param data the declaration 1503 * @throws Exception if an error occurs 1504 */ 1505 @Override 1506 protected void initBeanInstance(Object bean, BeanDeclaration data) 1507 throws Exception 1508 { 1509 FileConfiguration config = (FileConfiguration) bean; 1510 config.setBasePath(((ConfigurationDeclaration) data) 1511 .getConfigurationBuilder().getConfigurationBasePath()); 1512 super.initBeanInstance(bean, data); 1513 } 1514 } 1515 1516 /** 1517 * A specialized configuration provider for XML configurations. This 1518 * implementation acts like a {@code FileConfigurationProvider}, but 1519 * it will copy all entity IDs that have been registered for the 1520 * configuration builder to the new XML configuration before it is loaded. 1521 * 1522 * @since 1.6 1523 */ 1524 public static class XMLConfigurationProvider extends FileConfigurationProvider 1525 { 1526 /** 1527 * Creates a new instance of {@code XMLConfigurationProvider}. 1528 */ 1529 public XMLConfigurationProvider() 1530 { 1531 super(XMLConfiguration.class); 1532 } 1533 1534 /** 1535 * Returns a new empty configuration instance. This implementation 1536 * performs some additional initialization specific to XML 1537 * configurations. 1538 * 1539 * @param decl the configuration declaration 1540 * @return the new configuration 1541 * @throws Exception if an error occurs 1542 */ 1543 @Override 1544 public AbstractConfiguration getEmptyConfiguration( 1545 ConfigurationDeclaration decl) throws Exception 1546 { 1547 XMLConfiguration config = (XMLConfiguration) super 1548 .getEmptyConfiguration(decl); 1549 1550 DefaultConfigurationBuilder builder = decl 1551 .getConfigurationBuilder(); 1552 EntityResolver resolver = builder.getEntityResolver(); 1553 if (resolver instanceof EntityRegistry) 1554 { 1555 // copy the registered entities 1556 config.getRegisteredEntities().putAll( 1557 builder.getRegisteredEntities()); 1558 } 1559 else 1560 { 1561 config.setEntityResolver(resolver); 1562 } 1563 return config; 1564 } 1565 } 1566 1567 /** 1568 * A specialized configuration provider for file based configurations that 1569 * can handle configuration sources whose concrete type depends on the 1570 * extension of the file to be loaded. One example is the 1571 * {@code properties} tag: if the file ends with ".xml" a 1572 * XMLPropertiesConfiguration object must be created, otherwise a 1573 * PropertiesConfiguration object. 1574 */ 1575 static class FileExtensionConfigurationProvider extends 1576 FileConfigurationProvider 1577 { 1578 /** 1579 * Stores the class to be created when the file extension matches. 1580 */ 1581 private Class<?> matchingClass; 1582 1583 /** 1584 * Stores the name of the class to be created when the file extension 1585 * matches. 1586 */ 1587 private String matchingClassName; 1588 1589 /** 1590 * Stores the class to be created when the file extension does not 1591 * match. 1592 */ 1593 private Class<?> defaultClass; 1594 1595 /** 1596 * Stores the name of the class to be created when the file extension 1597 * does not match. 1598 */ 1599 private String defaultClassName; 1600 1601 /** Stores the file extension to be checked against. */ 1602 private String fileExtension; 1603 1604 /** 1605 * Creates a new instance of 1606 * {@code FileExtensionConfigurationProvider} and initializes it. 1607 * 1608 * @param matchingClass the class to be created when the file extension 1609 * matches 1610 * @param defaultClass the class to be created when the file extension 1611 * does not match 1612 * @param extension the file extension to be checked against 1613 */ 1614 public FileExtensionConfigurationProvider(Class<?> matchingClass, 1615 Class<?> defaultClass, String extension) 1616 { 1617 this.matchingClass = matchingClass; 1618 this.defaultClass = defaultClass; 1619 fileExtension = extension; 1620 } 1621 1622 /** 1623 * Creates a new instance of 1624 * {@code FileExtensionConfigurationProvider} and initializes it 1625 * with the names of the classes to be created. 1626 * 1627 * @param matchingClassName the name of the class to be created when the 1628 * file extension matches 1629 * @param defaultClassName the name of the class to be created when the 1630 * file extension does not match 1631 * @param extension the file extension to be checked against 1632 * @since 1.4 1633 */ 1634 public FileExtensionConfigurationProvider(String matchingClassName, 1635 String defaultClassName, String extension) 1636 { 1637 this.matchingClassName = matchingClassName; 1638 this.defaultClassName = defaultClassName; 1639 fileExtension = extension; 1640 } 1641 1642 /** 1643 * Returns the matching class object, no matter whether it was defined 1644 * as a class or as a class name. 1645 * 1646 * @return the matching class object 1647 * @throws Exception if an error occurs 1648 * @since 1.4 1649 */ 1650 protected synchronized Class<?> fetchMatchingClass() throws Exception 1651 { 1652 if (matchingClass == null) 1653 { 1654 matchingClass = loadClass(matchingClassName); 1655 } 1656 return matchingClass; 1657 } 1658 1659 /** 1660 * Returns the default class object, no matter whether it was defined as 1661 * a class or as a class name. 1662 * 1663 * @return the default class object 1664 * @throws Exception if an error occurs 1665 * @since 1.4 1666 */ 1667 protected synchronized Class<?> fetchDefaultClass() throws Exception 1668 { 1669 if (defaultClass == null) 1670 { 1671 defaultClass = loadClass(defaultClassName); 1672 } 1673 return defaultClass; 1674 } 1675 1676 /** 1677 * Creates the configuration object. The class is determined by the file 1678 * name's extension. 1679 * 1680 * @param beanClass the class 1681 * @param data the bean declaration 1682 * @return the new bean 1683 * @throws Exception if an error occurs 1684 */ 1685 @Override 1686 protected Object createBeanInstance(Class<?> beanClass, 1687 BeanDeclaration data) throws Exception 1688 { 1689 String fileName = ((ConfigurationDeclaration) data) 1690 .getConfiguration().getString(ATTR_FILENAME); 1691 if (fileName != null 1692 && fileName.toLowerCase().trim().endsWith(fileExtension)) 1693 { 1694 return super.createBeanInstance(fetchMatchingClass(), data); 1695 } 1696 else 1697 { 1698 return super.createBeanInstance(fetchDefaultClass(), data); 1699 } 1700 } 1701 } 1702 1703 /** 1704 * A specialized configuration provider class that allows to include other 1705 * configuration definition files. 1706 */ 1707 static class ConfigurationBuilderProvider extends ConfigurationProvider 1708 { 1709 /** 1710 * Creates a new instance of {@code ConfigurationBuilderProvider}. 1711 */ 1712 public ConfigurationBuilderProvider() 1713 { 1714 super(DefaultConfigurationBuilder.class); 1715 } 1716 1717 /** 1718 * Creates the configuration. First creates a configuration builder 1719 * object. Then returns the configuration created by this builder. 1720 * 1721 * @param decl the configuration declaration 1722 * @return the configuration 1723 * @exception Exception if an error occurs 1724 */ 1725 @Override 1726 public AbstractConfiguration getConfiguration( 1727 ConfigurationDeclaration decl) throws Exception 1728 { 1729 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super 1730 .getConfiguration(decl); 1731 return builder.getConfiguration(true); 1732 } 1733 1734 /** 1735 * Returns an empty configuration in case of an optional configuration 1736 * could not be created. This implementation returns an empty combined 1737 * configuration. 1738 * 1739 * @param decl the configuration declaration 1740 * @return the configuration 1741 * @exception Exception if an error occurs 1742 * @since 1.4 1743 */ 1744 @Override 1745 public AbstractConfiguration getEmptyConfiguration( 1746 ConfigurationDeclaration decl) throws Exception 1747 { 1748 return new CombinedConfiguration(); 1749 } 1750 1751 /** 1752 * {@inheritDoc} This implementation ensures that the configuration 1753 * builder created by this provider inherits the properties from the 1754 * current configuration builder. 1755 */ 1756 @Override 1757 protected void initBeanInstance(Object bean, BeanDeclaration data) 1758 throws Exception 1759 { 1760 ConfigurationDeclaration decl = (ConfigurationDeclaration) data; 1761 initChildBuilder(decl.getConfigurationBuilder(), 1762 (DefaultConfigurationBuilder) bean); 1763 super.initBeanInstance(bean, data); 1764 } 1765 1766 /** 1767 * Initializes the given child configuration builder from its parent 1768 * builder. This method copies the values of some properties from the 1769 * parent builder to the child builder so that the child inherits 1770 * properties from its parent. 1771 * 1772 * @param parent the parent builder 1773 * @param child the child builder 1774 */ 1775 private static void initChildBuilder( 1776 DefaultConfigurationBuilder parent, 1777 DefaultConfigurationBuilder child) 1778 { 1779 child.setAttributeSplittingDisabled(parent 1780 .isAttributeSplittingDisabled()); 1781 child.setBasePath(parent.getBasePath()); 1782 child.setDelimiterParsingDisabled(parent 1783 .isDelimiterParsingDisabled()); 1784 child.setListDelimiter(parent.getListDelimiter()); 1785 child.setThrowExceptionOnMissing(parent.isThrowExceptionOnMissing()); 1786 child.setLogger(parent.getLogger()); 1787 1788 child.clearConfigurationListeners(); 1789 for (ConfigurationListener l : parent.getConfigurationListeners()) 1790 { 1791 child.addConfigurationListener(l); 1792 } 1793 child.clearErrorListeners(); 1794 for (ConfigurationErrorListener l : parent.getErrorListeners()) 1795 { 1796 child.addErrorListener(l); 1797 } 1798 } 1799 } 1800}