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.configuration2.builder.combined; 018 019import java.net.URL; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.LinkedList; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.commons.configuration2.CombinedConfiguration; 031import org.apache.commons.configuration2.Configuration; 032import org.apache.commons.configuration2.ConfigurationLookup; 033import org.apache.commons.configuration2.HierarchicalConfiguration; 034import org.apache.commons.configuration2.SystemConfiguration; 035import org.apache.commons.configuration2.XMLConfiguration; 036import org.apache.commons.configuration2.beanutils.BeanDeclaration; 037import org.apache.commons.configuration2.beanutils.BeanHelper; 038import org.apache.commons.configuration2.beanutils.CombinedBeanDeclaration; 039import org.apache.commons.configuration2.beanutils.XMLBeanDeclaration; 040import org.apache.commons.configuration2.builder.BasicBuilderParameters; 041import org.apache.commons.configuration2.builder.BasicConfigurationBuilder; 042import org.apache.commons.configuration2.builder.BuilderParameters; 043import org.apache.commons.configuration2.builder.ConfigurationBuilder; 044import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent; 045import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl; 046import org.apache.commons.configuration2.builder.FileBasedBuilderProperties; 047import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; 048import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl; 049import org.apache.commons.configuration2.builder.XMLBuilderProperties; 050import org.apache.commons.configuration2.event.EventListener; 051import org.apache.commons.configuration2.ex.ConfigurationException; 052import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; 053import org.apache.commons.configuration2.interpol.Lookup; 054import org.apache.commons.configuration2.io.FileSystem; 055import org.apache.commons.configuration2.resolver.CatalogResolver; 056import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols; 057import org.apache.commons.configuration2.tree.OverrideCombiner; 058import org.apache.commons.configuration2.tree.UnionCombiner; 059import org.xml.sax.EntityResolver; 060 061/** 062 * <p> 063 * A specialized {@code ConfigurationBuilder} implementation that creates a 064 * {@link CombinedConfiguration} from multiple configuration sources defined by 065 * an XML-based <em>configuration definition file</em>. 066 * </p> 067 * <p> 068 * This class provides an easy and flexible means for loading multiple 069 * configuration sources and combining the results into a single configuration 070 * object. The sources to be loaded are defined in an XML document that can 071 * contain certain tags representing the different supported configuration 072 * classes. If such a tag is found, a corresponding {@code ConfigurationBuilder} 073 * class is instantiated and initialized using the classes of the 074 * {@code beanutils} package (namely 075 * {@link org.apache.commons.configuration2.beanutils.XMLBeanDeclaration 076 * XMLBeanDeclaration} will be used to extract the configuration's 077 * initialization parameters, which allows for complex initialization 078 * scenarios). 079 * </p> 080 * <p> 081 * It is also possible to add custom tags to the configuration definition file. 082 * For this purpose an implementation of 083 * {@link CombinedConfigurationBuilderProvider} has to be created which is 084 * responsible for the creation of a {@code ConfigurationBuilder} associated 085 * with the custom tag. An instance of this class has to be registered at the 086 * {@link CombinedBuilderParametersImpl} object which is used to initialize this 087 * {@code CombinedConfigurationBuilder}. This provider will then be called when 088 * the corresponding custom tag is detected. For many default configuration 089 * classes providers are already registered. 090 * </p> 091 * <p> 092 * The configuration definition file has the following basic structure: 093 * </p> 094 * 095 * <pre> 096 * <configuration systemProperties="properties file name"> 097 * <header> 098 * <!-- Optional meta information about the combined configuration --> 099 * </header> 100 * <override> 101 * <!-- Declarations for override configurations --> 102 * </override> 103 * <additional> 104 * <!-- Declarations for union configurations --> 105 * </additional> 106 * </configuration> 107 * </pre> 108 * 109 * <p> 110 * The name of the root element (here {@code configuration}) is arbitrary. The 111 * optional {@code systemProperties} attribute identifies the path to a property 112 * file containing properties that should be added to the system properties. If 113 * specified on the root element, the system properties are set before the rest 114 * of the configuration is processed. 115 * </p> 116 * <p> 117 * There are two sections (both of them are optional) for declaring 118 * <em>override</em> and <em>additional</em> configurations. Configurations in 119 * the former section are evaluated in the order of their declaration, and 120 * properties of configurations declared earlier hide those of configurations 121 * declared later. Configurations in the latter section are combined to a union 122 * configuration, i.e. all of their properties are added to a large hierarchical 123 * configuration. Configuration declarations that occur as direct children of 124 * the root element are treated as override declarations. 125 * </p> 126 * <p> 127 * Each configuration declaration consists of a tag whose name is associated 128 * with a {@code CombinedConfigurationBuilderProvider}. This can be one of the 129 * predefined tags like {@code properties}, or {@code xml}, or a custom tag, for 130 * which a configuration builder provider was registered (as described above). 131 * Attributes and sub elements with specific initialization parameters can be 132 * added. There are some reserved attributes with a special meaning that can be 133 * used in every configuration declaration: 134 * </p> 135 * <table border="1"> 136 * <caption>Standard attributes for configuration declarations</caption> 137 * <tr> 138 * <th>Attribute</th> 139 * <th>Meaning</th> 140 * </tr> 141 * <tr> 142 * <td valign="top">{@code config-name}</td> 143 * <td>Allows specifying a name for this configuration. This name can be used to 144 * obtain a reference to the configuration from the resulting combined 145 * configuration (see below). It can also be passed to the 146 * {@link #getNamedBuilder(String)} method.</td> 147 * </tr> 148 * <tr> 149 * <td valign="top">{@code config-at}</td> 150 * <td>With this attribute an optional prefix can be specified for the 151 * properties of the corresponding configuration.</td> 152 * </tr> 153 * <tr> 154 * <td valign="top">{@code config-optional}</td> 155 * <td>Declares a configuration source as optional. This means that errors that 156 * occur when creating the configuration are ignored.</td> 157 * </tr> 158 * <tr> 159 * <td valign="top">{@code config-reload}</td> 160 * <td>Many configuration sources support a reloading mechanism. For those 161 * sources it is possible to enable reloading by providing this attribute with a 162 * value of <strong>true</strong>.</td> 163 * </tr> 164 * </table> 165 * <p> 166 * The optional <em>header</em> section can contain some meta data about the 167 * created configuration itself. For instance, it is possible to set further 168 * properties of the {@code NodeCombiner} objects used for constructing the 169 * resulting configuration. 170 * </p> 171 * <p> 172 * The default configuration object returned by this builder is an instance of 173 * the {@link CombinedConfiguration} class. This allows for convenient access to 174 * the configuration objects maintained by the combined configuration (e.g. for 175 * updates of single configuration objects). It has also the advantage that the 176 * properties stored in all declared configuration objects are collected and 177 * transformed into a single hierarchical structure, which can be accessed using 178 * different expression engines. The actual {@code CombinedConfiguration} 179 * implementation can be overridden by specifying the class in the 180 * <em>config-class</em> attribute of the result element. 181 * </p> 182 * <p> 183 * A custom EntityResolver can be used for all XMLConfigurations by adding 184 * </p> 185 * 186 * <pre> 187 * <entity-resolver config-class="EntityResolver fully qualified class name"> 188 * </pre> 189 * 190 * <p> 191 * A specific CatalogResolver can be specified for all XMLConfiguration sources 192 * by adding 193 * </p> 194 * <pre> 195 * <entity-resolver catalogFiles="comma separated list of catalog files"> 196 * </pre> 197 * 198 * <p> 199 * Additional ConfigurationProviders can be added by configuring them in the 200 * <em>header</em> section. 201 * </p> 202 * 203 * <pre> 204 * <providers> 205 * <provider config-tag="tag name" config-class="provider fully qualified class name"/> 206 * </providers> 207 * </pre> 208 * 209 * <p> 210 * Additional variable resolvers can be added by configuring them in the 211 * <em>header</em> section. 212 * </p> 213 * 214 * <pre> 215 * <lookups> 216 * <lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/> 217 * </lookups> 218 * </pre> 219 * 220 * <p> 221 * All declared override configurations are directly added to the resulting 222 * combined configuration. If they are given names (using the 223 * {@code config-name} attribute), they can directly be accessed using the 224 * {@code getConfiguration(String)} method of {@code CombinedConfiguration}. The 225 * additional configurations are altogether added to another combined 226 * configuration, which uses a union combiner. Then this union configuration is 227 * added to the resulting combined configuration under the name defined by the 228 * {@code ADDITIONAL_NAME} constant. The {@link #getNamedBuilder(String)} method 229 * can be used to access the {@code ConfigurationBuilder} objects for all 230 * configuration sources which have been assigned a name; care has to be taken 231 * that these names are unique. 232 * </p> 233 * 234 * @since 1.3 235 * @author <a 236 * href="http://commons.apache.org/configuration/team-list.html">Commons 237 * Configuration team</a> 238 */ 239public class CombinedConfigurationBuilder extends BasicConfigurationBuilder<CombinedConfiguration> 240{ 241 /** 242 * Constant for the name of the additional configuration. If the 243 * configuration definition file contains an {@code additional} 244 * section, a special union configuration is created and added under this 245 * name to the resulting combined configuration. 246 */ 247 public static final String ADDITIONAL_NAME = CombinedConfigurationBuilder.class 248 .getName() 249 + "/ADDITIONAL_CONFIG"; 250 251 /** Constant for the name of the configuration bean factory. */ 252 static final String CONFIG_BEAN_FACTORY_NAME = CombinedConfigurationBuilder.class 253 .getName() 254 + ".CONFIG_BEAN_FACTORY_NAME"; 255 256 /** Constant for the reserved name attribute. */ 257 static final String ATTR_NAME = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 258 + XMLBeanDeclaration.RESERVED_PREFIX 259 + "name" 260 + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 261 262 /** Constant for the name of the at attribute. */ 263 static final String ATTR_ATNAME = "at"; 264 265 /** Constant for the reserved at attribute. */ 266 static final String ATTR_AT_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 267 + XMLBeanDeclaration.RESERVED_PREFIX 268 + ATTR_ATNAME 269 + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 270 271 /** Constant for the at attribute without the reserved prefix. */ 272 static final String ATTR_AT = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 273 + ATTR_ATNAME + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 274 275 /** Constant for the name of the optional attribute. */ 276 static final String ATTR_OPTIONALNAME = "optional"; 277 278 /** Constant for the reserved optional attribute. */ 279 static final String ATTR_OPTIONAL_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 280 + XMLBeanDeclaration.RESERVED_PREFIX 281 + ATTR_OPTIONALNAME 282 + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 283 284 /** Constant for the optional attribute without the reserved prefix. */ 285 static final String ATTR_OPTIONAL = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 286 + ATTR_OPTIONALNAME + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 287 288 /** Constant for the forceCreate attribute. */ 289 static final String ATTR_FORCECREATE = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 290 + XMLBeanDeclaration.RESERVED_PREFIX 291 + "forceCreate" 292 + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 293 294 /** Constant for the reload attribute. */ 295 static final String ATTR_RELOAD = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START 296 + XMLBeanDeclaration.RESERVED_PREFIX 297 + "reload" 298 + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END; 299 300 /** 301 * Constant for the tag attribute for providers. 302 */ 303 static final String KEY_SYSTEM_PROPS = "[@systemProperties]"; 304 305 /** Constant for the name of the header section. */ 306 static final String SEC_HEADER = "header"; 307 308 /** Constant for an expression that selects the union configurations. */ 309 static final String KEY_UNION = "additional"; 310 311 /** An array with the names of top level configuration sections.*/ 312 static final String[] CONFIG_SECTIONS = { 313 "additional", "override", SEC_HEADER 314 }; 315 316 /** 317 * Constant for an expression that selects override configurations in the 318 * override section. 319 */ 320 static final String KEY_OVERRIDE = "override"; 321 322 /** 323 * Constant for the key that points to the list nodes definition of the 324 * override combiner. 325 */ 326 static final String KEY_OVERRIDE_LIST = SEC_HEADER 327 + ".combiner.override.list-nodes.node"; 328 329 /** 330 * Constant for the key that points to the list nodes definition of the 331 * additional combiner. 332 */ 333 static final String KEY_ADDITIONAL_LIST = SEC_HEADER 334 + ".combiner.additional.list-nodes.node"; 335 336 /** 337 * Constant for the key for defining providers in the configuration file. 338 */ 339 static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER 340 + ".providers.provider"; 341 342 /** 343 * Constant for the tag attribute for providers. 344 */ 345 static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]"; 346 347 /** 348 * Constant for the key for defining variable resolvers 349 */ 350 static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER 351 + ".lookups.lookup"; 352 353 /** 354 * Constant for the key for defining entity resolvers 355 */ 356 static final String KEY_ENTITY_RESOLVER = SEC_HEADER + ".entity-resolver"; 357 358 /** 359 * Constant for the prefix attribute for lookups. 360 */ 361 static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]"; 362 363 /** 364 * Constant for the FileSystem. 365 */ 366 static final String FILE_SYSTEM = SEC_HEADER + ".fileSystem"; 367 368 /** 369 * Constant for the key of the result declaration. This key can point to a 370 * bean declaration, which defines properties of the resulting combined 371 * configuration. 372 */ 373 static final String KEY_RESULT = SEC_HEADER + ".result"; 374 375 /** Constant for the key of the combiner in the result declaration.*/ 376 static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner"; 377 378 /** Constant for the XML file extension. */ 379 static final String EXT_XML = "xml"; 380 381 /** Constant for the basic configuration builder class. */ 382 private static final String BASIC_BUILDER = 383 "org.apache.commons.configuration2.builder.BasicConfigurationBuilder"; 384 385 /** Constant for the file-based configuration builder class. */ 386 private static final String FILE_BUILDER = 387 "org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder"; 388 389 /** Constant for the reloading file-based configuration builder class. */ 390 private static final String RELOADING_BUILDER = 391 "org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder"; 392 393 /** Constant for the name of the file-based builder parameters class. */ 394 private static final String FILE_PARAMS = 395 "org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl"; 396 397 /** Constant for the provider for properties files. */ 398 private static final ConfigurationBuilderProvider PROPERTIES_PROVIDER = 399 new FileExtensionConfigurationBuilderProvider( 400 FILE_BUILDER, 401 RELOADING_BUILDER, 402 "org.apache.commons.configuration2.XMLPropertiesConfiguration", 403 "org.apache.commons.configuration2.PropertiesConfiguration", 404 EXT_XML, Collections.singletonList(FILE_PARAMS)); 405 406 /** Constant for the provider for XML files. */ 407 private static final ConfigurationBuilderProvider XML_PROVIDER = 408 new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER, 409 "org.apache.commons.configuration2.XMLConfiguration", 410 Collections.singletonList("org.apache.commons.configuration2.builder.XMLBuilderParametersImpl")); 411 412 /** Constant for the provider for JNDI sources. */ 413 private static final BaseConfigurationBuilderProvider JNDI_PROVIDER = 414 new BaseConfigurationBuilderProvider( 415 BASIC_BUILDER, 416 null, 417 "org.apache.commons.configuration2.JNDIConfiguration", 418 Collections.singletonList("org.apache.commons.configuration2.builder.JndiBuilderParametersImpl")); 419 420 /** Constant for the provider for system properties. */ 421 private static final BaseConfigurationBuilderProvider SYSTEM_PROVIDER = 422 new BaseConfigurationBuilderProvider( 423 BASIC_BUILDER, 424 null, 425 "org.apache.commons.configuration2.SystemConfiguration", 426 Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters")); 427 428 /** Constant for the provider for ini files. */ 429 private static final BaseConfigurationBuilderProvider INI_PROVIDER = 430 new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER, 431 "org.apache.commons.configuration2.INIConfiguration", 432 Collections.singletonList(FILE_PARAMS)); 433 434 /** Constant for the provider for environment properties. */ 435 private static final BaseConfigurationBuilderProvider ENV_PROVIDER = 436 new BaseConfigurationBuilderProvider( 437 BASIC_BUILDER, 438 null, 439 "org.apache.commons.configuration2.EnvironmentConfiguration", 440 Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters")); 441 442 /** Constant for the provider for plist files. */ 443 private static final BaseConfigurationBuilderProvider PLIST_PROVIDER = 444 new FileExtensionConfigurationBuilderProvider( 445 FILE_BUILDER, 446 RELOADING_BUILDER, 447 "org.apache.commons.configuration2.plist.XMLPropertyListConfiguration", 448 "org.apache.commons.configuration2.plist.PropertyListConfiguration", 449 EXT_XML, Collections.singletonList(FILE_PARAMS)); 450 451 /** Constant for the provider for configuration definition files. */ 452 private static final BaseConfigurationBuilderProvider COMBINED_PROVIDER = 453 new CombinedConfigurationBuilderProvider(); 454 455 /** Constant for the provider for multiple XML configurations. */ 456 private static final MultiFileConfigurationBuilderProvider MULTI_XML_PROVIDER = 457 new MultiFileConfigurationBuilderProvider( 458 "org.apache.commons.configuration2.XMLConfiguration", 459 "org.apache.commons.configuration2.builder.XMLBuilderParametersImpl"); 460 461 /** An array with the names of the default tags. */ 462 private static final String[] DEFAULT_TAGS = { 463 "properties", "xml", "hierarchicalXml", "plist", 464 "ini", "system", "env", "jndi", "configuration", "multiFile" 465 }; 466 467 /** An array with the providers for the default tags. */ 468 private static final ConfigurationBuilderProvider[] DEFAULT_PROVIDERS = { 469 PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, PLIST_PROVIDER, INI_PROVIDER, 470 SYSTEM_PROVIDER, ENV_PROVIDER, JNDI_PROVIDER, COMBINED_PROVIDER, 471 MULTI_XML_PROVIDER 472 }; 473 474 /** A map with the default configuration builder providers. */ 475 private static final Map<String, ConfigurationBuilderProvider> DEFAULT_PROVIDERS_MAP; 476 477 /** The builder for the definition configuration. */ 478 private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder; 479 480 /** Stores temporarily the configuration with the builder definitions. */ 481 private HierarchicalConfiguration<?> definitionConfiguration; 482 483 /** The object with data about configuration sources. */ 484 private ConfigurationSourceData sourceData; 485 486 /** Stores the current parameters object. */ 487 private CombinedBuilderParametersImpl currentParameters; 488 489 /** The current XML parameters object. */ 490 private XMLBuilderParametersImpl currentXMLParameters; 491 492 /** The configuration that is currently constructed. */ 493 private CombinedConfiguration currentConfiguration; 494 495 /** 496 * A {@code ConfigurationInterpolator} to be used as parent for all child 497 * configurations to enable cross-source interpolation. 498 */ 499 private ConfigurationInterpolator parentInterpolator; 500 501 /** 502 * Creates a new instance of {@code CombinedConfigurationBuilder}. No parameters 503 * are set. 504 */ 505 public CombinedConfigurationBuilder() 506 { 507 super(CombinedConfiguration.class); 508 } 509 510 /** 511 * 512 * Creates a new instance of {@code CombinedConfigurationBuilder} and sets 513 * the specified initialization parameters. 514 * @param params a map with initialization parameters 515 */ 516 public CombinedConfigurationBuilder(final Map<String, Object> params) 517 { 518 super(CombinedConfiguration.class, params); 519 } 520 521 /** 522 * 523 * Creates a new instance of {@code CombinedConfigurationBuilder} and sets 524 * the specified initialization parameters and the <em>allowFailOnInit</em> flag. 525 * @param params a map with initialization parameters 526 * @param allowFailOnInit the <em>allowFailOnInit</em> flag 527 */ 528 public CombinedConfigurationBuilder(final Map<String, Object> params, final boolean allowFailOnInit) 529 { 530 super(CombinedConfiguration.class, params, allowFailOnInit); 531 } 532 533 /** 534 * Returns the {@code ConfigurationBuilder} which creates the definition 535 * configuration. 536 * 537 * @return the builder for the definition configuration 538 * @throws ConfigurationException if an error occurs 539 */ 540 public synchronized ConfigurationBuilder<? extends HierarchicalConfiguration<?>> getDefinitionBuilder() 541 throws ConfigurationException 542 { 543 if (definitionBuilder == null) 544 { 545 definitionBuilder = setupDefinitionBuilder(getParameters()); 546 addDefinitionBuilderChangeListener(definitionBuilder); 547 } 548 return definitionBuilder; 549 } 550 551 /** 552 * {@inheritDoc} This method is overridden to adapt the return type. 553 */ 554 @Override 555 public CombinedConfigurationBuilder configure(final BuilderParameters... params) 556 { 557 super.configure(params); 558 return this; 559 } 560 561 /** 562 * <p> 563 * Returns the configuration builder with the given name. With this method a 564 * builder of a child configuration which was given a name in the 565 * configuration definition file can be accessed directly. 566 * </p> 567 * <p> 568 * <strong>Important note:</strong> This method only returns a meaningful 569 * result after the result configuration has been created by calling 570 * {@code getConfiguration()}. If called before, always an exception is 571 * thrown. 572 * </p> 573 * 574 * @param name the name of the builder in question 575 * @return the child configuration builder with this name 576 * @throws ConfigurationException if information about named builders is not 577 * yet available or no builder with this name exists 578 */ 579 public synchronized ConfigurationBuilder<? extends Configuration> getNamedBuilder( 580 final String name) throws ConfigurationException 581 { 582 if (sourceData == null) 583 { 584 throw new ConfigurationException("Information about child builders" 585 + " has not been setup yet! Call getConfiguration() first."); 586 } 587 final ConfigurationBuilder<? extends Configuration> builder = 588 sourceData.getNamedBuilder(name); 589 if (builder == null) 590 { 591 throw new ConfigurationException("Builder cannot be resolved: " 592 + name); 593 } 594 return builder; 595 } 596 597 /** 598 * <p> 599 * Returns a set with the names of all child configuration builders. A tag 600 * defining a configuration source in the configuration definition file can 601 * have the {@code config-name} attribute. If this attribute is present, the 602 * corresponding builder is assigned this name and can be directly accessed 603 * through the {@link #getNamedBuilder(String)} method. This method returns 604 * a collection with all available builder names. 605 * </p> 606 * <p> 607 * <strong>Important note:</strong> This method only returns a meaningful 608 * result after the result configuration has been created by calling 609 * {@code getConfiguration()}. If called before, always an empty set is 610 * returned. 611 * </p> 612 * 613 * @return a set with the names of all builders 614 */ 615 public synchronized Set<String> builderNames() 616 { 617 if (sourceData == null) 618 { 619 return Collections.emptySet(); 620 } 621 return Collections.unmodifiableSet(sourceData.builderNames()); 622 } 623 624 /** 625 * {@inheritDoc} This implementation resets some specific internal state of 626 * this builder. 627 */ 628 @Override 629 public synchronized void resetParameters() 630 { 631 super.resetParameters(); 632 definitionBuilder = null; 633 definitionConfiguration = null; 634 currentParameters = null; 635 currentXMLParameters = null; 636 637 if (sourceData != null) 638 { 639 sourceData.cleanUp(); 640 sourceData = null; 641 } 642 } 643 644 /** 645 * Obtains the {@code ConfigurationBuilder} object which provides access to 646 * the configuration containing the definition of the combined configuration 647 * to create. If a definition builder is defined in the parameters, it is 648 * used. Otherwise, we check whether the combined builder parameters object 649 * contains a parameters object for the definition builder. If this is the 650 * case, a builder for an {@code XMLConfiguration} is created and configured 651 * with this object. As a last resort, it is looked for a 652 * {@link FileBasedBuilderParametersImpl} object in the properties. If 653 * found, also a XML configuration builder is created which loads this file. 654 * Note: This method is called from a synchronized block. 655 * 656 * @param params the current parameters for this builder 657 * @return the builder for the definition configuration 658 * @throws ConfigurationException if an error occurs 659 */ 660 protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> setupDefinitionBuilder( 661 final Map<String, Object> params) throws ConfigurationException 662 { 663 final CombinedBuilderParametersImpl cbParams = 664 CombinedBuilderParametersImpl.fromParameters(params); 665 if (cbParams != null) 666 { 667 final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = 668 cbParams.getDefinitionBuilder(); 669 if (defBuilder != null) 670 { 671 return defBuilder; 672 } 673 674 if (cbParams.getDefinitionBuilderParameters() != null) 675 { 676 return createXMLDefinitionBuilder(cbParams 677 .getDefinitionBuilderParameters()); 678 } 679 } 680 681 final BuilderParameters fileParams = 682 FileBasedBuilderParametersImpl.fromParameters(params); 683 if (fileParams != null) 684 { 685 return createXMLDefinitionBuilder(fileParams); 686 } 687 688 throw new ConfigurationException( 689 "No builder for configuration definition specified!"); 690 } 691 692 /** 693 * Creates a default builder for the definition configuration and 694 * initializes it with a parameters object. This method is called if no 695 * definition builder is defined in this builder's parameters. This 696 * implementation creates a default file-based builder which produces an 697 * {@code XMLConfiguration}; it expects a corresponding file specification. 698 * Note: This method is called in a synchronized block. 699 * 700 * @param builderParams the parameters object for the builder 701 * @return the standard builder for the definition configuration 702 */ 703 protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder( 704 final BuilderParameters builderParams) 705 { 706 return new FileBasedConfigurationBuilder<>( 707 XMLConfiguration.class).configure(builderParams); 708 } 709 710 /** 711 * Returns the configuration containing the definition of the combined 712 * configuration to be created. This method only returns a defined result 713 * during construction of the result configuration. The definition 714 * configuration is obtained from the definition builder at first access and 715 * then stored temporarily to ensure that during result construction always 716 * the same configuration instance is used. (Otherwise, it would be possible 717 * that the definition builder returns a different instance when queried 718 * multiple times.) 719 * 720 * @return the definition configuration 721 * @throws ConfigurationException if an error occurs 722 */ 723 protected HierarchicalConfiguration<?> getDefinitionConfiguration() 724 throws ConfigurationException 725 { 726 if (definitionConfiguration == null) 727 { 728 definitionConfiguration = getDefinitionBuilder().getConfiguration(); 729 } 730 return definitionConfiguration; 731 } 732 733 /** 734 * Returns a collection with the builders for all child configuration 735 * sources. This method can be used by derived classes providing additional 736 * functionality on top of the declared configuration sources. It only 737 * returns a defined value during construction of the result configuration 738 * instance. 739 * 740 * @return a collection with the builders for child configuration sources 741 */ 742 protected synchronized Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders() 743 { 744 return sourceData.getChildBuilders(); 745 } 746 747 /** 748 * {@inheritDoc} This implementation evaluates the {@code result} property 749 * of the definition configuration. It creates a combined bean declaration 750 * with both the properties specified in the definition file and the 751 * properties defined as initialization parameters. 752 */ 753 @Override 754 protected BeanDeclaration createResultDeclaration(final Map<String, Object> params) 755 throws ConfigurationException 756 { 757 final BeanDeclaration paramsDecl = super.createResultDeclaration(params); 758 final XMLBeanDeclaration resultDecl = 759 new XMLBeanDeclaration(getDefinitionConfiguration(), 760 KEY_RESULT, true, CombinedConfiguration.class.getName()); 761 return new CombinedBeanDeclaration(resultDecl, paramsDecl); 762 } 763 764 /** 765 * {@inheritDoc} This implementation processes the definition configuration 766 * in order to 767 * <ul> 768 * <li>initialize the resulting {@code CombinedConfiguration}</li> 769 * <li>determine the builders for all configuration sources</li> 770 * <li>populate the resulting {@code CombinedConfiguration}</li> 771 * </ul> 772 */ 773 @Override 774 protected void initResultInstance(final CombinedConfiguration result) 775 throws ConfigurationException 776 { 777 super.initResultInstance(result); 778 779 currentConfiguration = result; 780 final HierarchicalConfiguration<?> config = getDefinitionConfiguration(); 781 if (config.getMaxIndex(KEY_COMBINER) < 0) 782 { 783 // No combiner defined => set default 784 result.setNodeCombiner(new OverrideCombiner()); 785 } 786 787 setUpCurrentParameters(); 788 initNodeCombinerListNodes(result, config, KEY_OVERRIDE_LIST); 789 registerConfiguredProviders(config); 790 setUpCurrentXMLParameters(); 791 currentXMLParameters.setFileSystem(initFileSystem(config)); 792 initSystemProperties(config, getBasePath()); 793 registerConfiguredLookups(config, result); 794 configureEntityResolver(config, currentXMLParameters); 795 setUpParentInterpolator(currentConfiguration, config); 796 797 final ConfigurationSourceData data = getSourceData(); 798 final boolean createBuilders = data.getChildBuilders().isEmpty(); 799 final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders = 800 data.createAndAddConfigurations(result, 801 data.getOverrideSources(), data.overrideBuilders); 802 if (createBuilders) 803 { 804 data.overrideBuilders.addAll(overrideBuilders); 805 } 806 if (!data.getUnionSources().isEmpty()) 807 { 808 final CombinedConfiguration addConfig = createAdditionalsConfiguration(result); 809 result.addConfiguration(addConfig, ADDITIONAL_NAME); 810 initNodeCombinerListNodes(addConfig, config, KEY_ADDITIONAL_LIST); 811 final List<ConfigurationBuilder<? extends Configuration>> unionBuilders = 812 data.createAndAddConfigurations(addConfig, 813 data.unionDeclarations, data.unionBuilders); 814 if (createBuilders) 815 { 816 data.unionBuilders.addAll(unionBuilders); 817 } 818 } 819 820 result.isEmpty(); // this sets up the node structure 821 currentConfiguration = null; 822 } 823 824 /** 825 * Creates the {@code CombinedConfiguration} for the configuration 826 * sources in the <code><additional></code> section. This method is 827 * called when the builder constructs the final configuration. It creates a 828 * new {@code CombinedConfiguration} and initializes some properties 829 * from the result configuration. 830 * 831 * @param resultConfig the result configuration (this is the configuration 832 * that will be returned by the builder) 833 * @return the {@code CombinedConfiguration} for the additional 834 * configuration sources 835 * @since 1.7 836 */ 837 protected CombinedConfiguration createAdditionalsConfiguration( 838 final CombinedConfiguration resultConfig) 839 { 840 final CombinedConfiguration addConfig = 841 new CombinedConfiguration(new UnionCombiner()); 842 addConfig.setListDelimiterHandler(resultConfig.getListDelimiterHandler()); 843 return addConfig; 844 } 845 846 /** 847 * Processes custom {@link Lookup} objects that might be declared in the 848 * definition configuration. Each {@code Lookup} object is registered at the 849 * definition configuration and at the result configuration. It is also 850 * added to all child configurations added to the resulting combined 851 * configuration. 852 * 853 * @param defConfig the definition configuration 854 * @param resultConfig the resulting configuration 855 * @throws ConfigurationException if an error occurs 856 */ 857 protected void registerConfiguredLookups( 858 final HierarchicalConfiguration<?> defConfig, final Configuration resultConfig) 859 throws ConfigurationException 860 { 861 final Map<String, Lookup> lookups = new HashMap<>(); 862 863 final List<? extends HierarchicalConfiguration<?>> nodes = 864 defConfig.configurationsAt(KEY_CONFIGURATION_LOOKUPS); 865 for (final HierarchicalConfiguration<?> config : nodes) 866 { 867 final XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 868 final String key = config.getString(KEY_LOOKUP_KEY); 869 final Lookup lookup = (Lookup) fetchBeanHelper().createBean(decl); 870 lookups.put(key, lookup); 871 } 872 873 if (!lookups.isEmpty()) 874 { 875 final ConfigurationInterpolator defCI = defConfig.getInterpolator(); 876 if (defCI != null) 877 { 878 defCI.registerLookups(lookups); 879 } 880 resultConfig.getInterpolator().registerLookups(lookups); 881 } 882 } 883 884 /** 885 * Creates and initializes a default {@code FileSystem} if the definition 886 * configuration contains a corresponding declaration. The file system 887 * returned by this method is used as default for all file-based child 888 * configuration sources. 889 * 890 * @param config the definition configuration 891 * @return the default {@code FileSystem} (may be <b>null</b>) 892 * @throws ConfigurationException if an error occurs 893 */ 894 protected FileSystem initFileSystem(final HierarchicalConfiguration<?> config) 895 throws ConfigurationException 896 { 897 if (config.getMaxIndex(FILE_SYSTEM) == 0) 898 { 899 final XMLBeanDeclaration decl = 900 new XMLBeanDeclaration(config, FILE_SYSTEM); 901 return (FileSystem) fetchBeanHelper().createBean(decl); 902 } 903 return null; 904 } 905 906 /** 907 * Handles a file with system properties that may be defined in the 908 * definition configuration. If such property file is configured, all of its 909 * properties are added to the system properties. 910 * 911 * @param config the definition configuration 912 * @param basePath the base path defined for this builder (may be 913 * <b>null</b>) 914 * @throws ConfigurationException if an error occurs. 915 */ 916 protected void initSystemProperties(final HierarchicalConfiguration<?> config, 917 final String basePath) throws ConfigurationException 918 { 919 final String fileName = config.getString(KEY_SYSTEM_PROPS); 920 if (fileName != null) 921 { 922 try 923 { 924 SystemConfiguration.setSystemProperties(basePath, fileName); 925 } 926 catch (final Exception ex) 927 { 928 throw new ConfigurationException( 929 "Error setting system properties from " + fileName, ex); 930 } 931 } 932 } 933 934 /** 935 * Creates and initializes a default {@code EntityResolver} if the 936 * definition configuration contains a corresponding declaration. 937 * 938 * @param config the definition configuration 939 * @param xmlParams the (already partly initialized) object with XML 940 * parameters; here the new resolver is to be stored 941 * @throws ConfigurationException if an error occurs 942 */ 943 protected void configureEntityResolver(final HierarchicalConfiguration<?> config, 944 final XMLBuilderParametersImpl xmlParams) throws ConfigurationException 945 { 946 if (config.getMaxIndex(KEY_ENTITY_RESOLVER) == 0) 947 { 948 final XMLBeanDeclaration decl = 949 new XMLBeanDeclaration(config, KEY_ENTITY_RESOLVER, true); 950 final EntityResolver resolver = 951 (EntityResolver) fetchBeanHelper().createBean(decl, 952 CatalogResolver.class); 953 final FileSystem fileSystem = xmlParams.getFileHandler().getFileSystem(); 954 if (fileSystem != null) 955 { 956 BeanHelper.setProperty(resolver, "fileSystem", fileSystem); 957 } 958 final String basePath = xmlParams.getFileHandler().getBasePath(); 959 if (basePath != null) 960 { 961 BeanHelper.setProperty(resolver, "baseDir", basePath); 962 } 963 final ConfigurationInterpolator ci = new ConfigurationInterpolator(); 964 ci.registerLookups(fetchPrefixLookups()); 965 BeanHelper.setProperty(resolver, "interpolator", ci); 966 967 xmlParams.setEntityResolver(resolver); 968 } 969 } 970 971 /** 972 * Returns the {@code ConfigurationBuilderProvider} for the given tag. This 973 * method is called during creation of the result configuration. (It is not 974 * allowed to call it at another point of time; result is then 975 * unpredictable!) It supports all default providers and custom providers 976 * added through the parameters object as well. 977 * 978 * @param tagName the name of the tag 979 * @return the provider that was registered for this tag or <b>null</b> if 980 * there is none 981 */ 982 protected ConfigurationBuilderProvider providerForTag(final String tagName) 983 { 984 return currentParameters.providerForTag(tagName); 985 } 986 987 /** 988 * Initializes a parameters object for a child builder. This combined 989 * configuration builder has a bunch of properties which may be inherited by 990 * child configurations, e.g. the base path, the file system, etc. While 991 * processing the builders for child configurations, this method is called 992 * for each parameters object for a child builder. It initializes some 993 * properties of the passed in parameters objects which are derived from 994 * this parent builder. 995 * 996 * @param params the parameters object to be initialized 997 */ 998 protected void initChildBuilderParameters(final BuilderParameters params) 999 { 1000 initDefaultChildParameters(params); 1001 1002 if (params instanceof BasicBuilderParameters) 1003 { 1004 initChildBasicParameters((BasicBuilderParameters) params); 1005 } 1006 if (params instanceof XMLBuilderProperties<?>) 1007 { 1008 initChildXMLParameters((XMLBuilderProperties<?>) params); 1009 } 1010 if (params instanceof FileBasedBuilderProperties<?>) 1011 { 1012 initChildFileBasedParameters((FileBasedBuilderProperties<?>) params); 1013 } 1014 if (params instanceof CombinedBuilderParametersImpl) 1015 { 1016 initChildCombinedParameters((CombinedBuilderParametersImpl) params); 1017 } 1018 } 1019 1020 /** 1021 * Initializes the event listeners of the specified builder from this 1022 * object. This method is used to inherit all listeners from a parent 1023 * builder. 1024 * 1025 * @param dest the destination builder object which is to be initialized 1026 */ 1027 void initChildEventListeners( 1028 final BasicConfigurationBuilder<? extends Configuration> dest) 1029 { 1030 copyEventListeners(dest); 1031 } 1032 1033 /** 1034 * Returns the configuration object that is currently constructed. This 1035 * method can be called during construction of the result configuration. It 1036 * is intended for internal usage, e.g. some specialized builder providers 1037 * need access to this configuration to perform advanced initialization. 1038 * 1039 * @return the configuration that us currently under construction 1040 */ 1041 CombinedConfiguration getConfigurationUnderConstruction() 1042 { 1043 return currentConfiguration; 1044 } 1045 1046 /** 1047 * Initializes a bean using the current {@code BeanHelper}. This is needed 1048 * by builder providers when the configuration objects for sub builders are 1049 * constructed. 1050 * 1051 * @param bean the bean to be initialized 1052 * @param decl the {@code BeanDeclaration} 1053 */ 1054 void initBean(final Object bean, final BeanDeclaration decl) 1055 { 1056 fetchBeanHelper().initBean(bean, decl); 1057 } 1058 1059 /** 1060 * Initializes the current parameters object. This object has either been 1061 * passed at builder configuration time or it is newly created. In any 1062 * case, it is manipulated during result creation. 1063 */ 1064 private void setUpCurrentParameters() 1065 { 1066 currentParameters = 1067 CombinedBuilderParametersImpl.fromParameters(getParameters(), true); 1068 currentParameters.registerMissingProviders(DEFAULT_PROVIDERS_MAP); 1069 } 1070 1071 /** 1072 * Sets up an XML parameters object which is used to store properties 1073 * related to XML and file-based configurations during creation of the 1074 * result configuration. The properties stored in this object can be 1075 * inherited to child configurations. 1076 * 1077 * @throws ConfigurationException if an error occurs 1078 */ 1079 private void setUpCurrentXMLParameters() throws ConfigurationException 1080 { 1081 currentXMLParameters = new XMLBuilderParametersImpl(); 1082 initDefaultBasePath(); 1083 } 1084 1085 /** 1086 * Sets up a parent {@code ConfigurationInterpolator} object. This object 1087 * has a default {@link Lookup} querying the resulting combined 1088 * configuration. Thus interpolation works globally across all configuration 1089 * sources. 1090 * 1091 * @param resultConfig the result configuration 1092 * @param defConfig the definition configuration 1093 */ 1094 private void setUpParentInterpolator(final Configuration resultConfig, 1095 final Configuration defConfig) 1096 { 1097 parentInterpolator = new ConfigurationInterpolator(); 1098 parentInterpolator.addDefaultLookup(new ConfigurationLookup( 1099 resultConfig)); 1100 final ConfigurationInterpolator defInterpolator = defConfig.getInterpolator(); 1101 if (defInterpolator != null) 1102 { 1103 defInterpolator.setParentInterpolator(parentInterpolator); 1104 } 1105 } 1106 1107 /** 1108 * Initializes the default base path for all file-based child configuration 1109 * sources. The base path can be explicitly defined in the parameters of 1110 * this builder. Otherwise, if the definition builder is a file-based 1111 * builder, it is obtained from there. 1112 * 1113 * @throws ConfigurationException if an error occurs 1114 */ 1115 private void initDefaultBasePath() throws ConfigurationException 1116 { 1117 assert currentParameters != null : "Current parameters undefined!"; 1118 if (currentParameters.getBasePath() != null) 1119 { 1120 currentXMLParameters.setBasePath(currentParameters.getBasePath()); 1121 } 1122 else 1123 { 1124 final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = 1125 getDefinitionBuilder(); 1126 if (defBuilder instanceof FileBasedConfigurationBuilder) 1127 { 1128 @SuppressWarnings("rawtypes") 1129 final 1130 FileBasedConfigurationBuilder fileBuilder = 1131 (FileBasedConfigurationBuilder) defBuilder; 1132 final URL url = fileBuilder.getFileHandler().getURL(); 1133 currentXMLParameters.setBasePath((url != null) ? url 1134 .toExternalForm() : fileBuilder.getFileHandler() 1135 .getBasePath()); 1136 } 1137 } 1138 } 1139 1140 /** 1141 * Executes the {@link org.apache.commons.configuration2.builder.DefaultParametersManager 1142 * DefaultParametersManager} stored in the current 1143 * parameters on the passed in parameters object. If default handlers have been 1144 * registered for this type of parameters, an initialization is now 1145 * performed. This method is called before the parameters object is 1146 * initialized from the configuration definition file. So default values 1147 * can be overridden later with concrete property definitions. 1148 * 1149 * @param params the parameters to be initialized 1150 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error 1151 * occurs when copying properties 1152 */ 1153 private void initDefaultChildParameters(final BuilderParameters params) 1154 { 1155 currentParameters.getChildDefaultParametersManager() 1156 .initializeParameters(params); 1157 } 1158 1159 /** 1160 * Initializes basic builder parameters for a child configuration with 1161 * default settings set for this builder. This implementation ensures that 1162 * all {@code Lookup} objects are propagated to child configurations and 1163 * interpolation is setup correctly. 1164 * 1165 * @param params the parameters object 1166 */ 1167 private void initChildBasicParameters(final BasicBuilderParameters params) 1168 { 1169 params.setPrefixLookups(fetchPrefixLookups()); 1170 params.setParentInterpolator(parentInterpolator); 1171 if (currentParameters.isInheritSettings()) 1172 { 1173 params.inheritFrom(getParameters()); 1174 } 1175 } 1176 1177 /** 1178 * Initializes a parameters object for a file-based configuration with 1179 * properties already set for this parent builder. This method handles 1180 * properties like a default file system or a base path. 1181 * 1182 * @param params the parameters object 1183 */ 1184 private void initChildFileBasedParameters( 1185 final FileBasedBuilderProperties<?> params) 1186 { 1187 params.setBasePath(getBasePath()); 1188 params.setFileSystem(currentXMLParameters.getFileHandler() 1189 .getFileSystem()); 1190 } 1191 1192 /** 1193 * Initializes a parameters object for an XML configuration with properties 1194 * already set for this parent builder. 1195 * 1196 * @param params the parameters object 1197 */ 1198 private void initChildXMLParameters(final XMLBuilderProperties<?> params) 1199 { 1200 params.setEntityResolver(currentXMLParameters.getEntityResolver()); 1201 } 1202 1203 /** 1204 * Initializes a parameters object for a combined configuration builder with 1205 * properties already set for this parent builder. This implementation deals 1206 * only with a subset of properties. Other properties are already handled by 1207 * the specialized builder provider. 1208 * 1209 * @param params the parameters object 1210 */ 1211 private void initChildCombinedParameters( 1212 final CombinedBuilderParametersImpl params) 1213 { 1214 params.registerMissingProviders(currentParameters); 1215 params.setBasePath(getBasePath()); 1216 } 1217 1218 /** 1219 * Obtains the data object for the configuration sources and the 1220 * corresponding builders. This object is created on first access and reset 1221 * when the definition builder sends a change event. This method is called 1222 * in a synchronized block. 1223 * 1224 * @return the object with information about configuration sources 1225 * @throws ConfigurationException if an error occurs 1226 */ 1227 private ConfigurationSourceData getSourceData() 1228 throws ConfigurationException 1229 { 1230 if (sourceData == null) 1231 { 1232 if (currentParameters == null) 1233 { 1234 setUpCurrentParameters(); 1235 setUpCurrentXMLParameters(); 1236 } 1237 sourceData = createSourceData(); 1238 } 1239 return sourceData; 1240 } 1241 1242 /** 1243 * Creates the data object for configuration sources and the corresponding 1244 * builders. 1245 * 1246 * @return the newly created data object 1247 * @throws ConfigurationException if an error occurs 1248 */ 1249 private ConfigurationSourceData createSourceData() 1250 throws ConfigurationException 1251 { 1252 final ConfigurationSourceData result = new ConfigurationSourceData(); 1253 result.initFromDefinitionConfiguration(getDefinitionConfiguration()); 1254 return result; 1255 } 1256 1257 /** 1258 * Returns the current base path of this configuration builder. This is used 1259 * for instance by all file-based child configurations. 1260 * 1261 * @return the base path 1262 */ 1263 private String getBasePath() 1264 { 1265 return currentXMLParameters.getFileHandler().getBasePath(); 1266 } 1267 1268 /** 1269 * Registers providers defined in the configuration. 1270 * 1271 * @param defConfig the definition configuration 1272 * @throws ConfigurationException if an error occurs 1273 */ 1274 private void registerConfiguredProviders(final HierarchicalConfiguration<?> defConfig) 1275 throws ConfigurationException 1276 { 1277 final List<? extends HierarchicalConfiguration<?>> nodes = 1278 defConfig.configurationsAt(KEY_CONFIGURATION_PROVIDERS); 1279 for (final HierarchicalConfiguration<?> config : nodes) 1280 { 1281 final XMLBeanDeclaration decl = new XMLBeanDeclaration(config); 1282 final String key = config.getString(KEY_PROVIDER_KEY); 1283 currentParameters.registerProvider(key, 1284 (ConfigurationBuilderProvider) fetchBeanHelper().createBean(decl)); 1285 } 1286 } 1287 1288 /** 1289 * Adds a listener at the given definition builder which resets this builder 1290 * when a reset of the definition builder happens. This way it is ensured 1291 * that this builder produces a new combined configuration when its 1292 * definition configuration changes. 1293 * 1294 * @param defBuilder the definition builder 1295 */ 1296 private void addDefinitionBuilderChangeListener( 1297 final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder) 1298 { 1299 defBuilder.addEventListener(ConfigurationBuilderEvent.RESET, 1300 new EventListener<ConfigurationBuilderEvent>() 1301 { 1302 @Override 1303 public void onEvent(final ConfigurationBuilderEvent event) 1304 { 1305 synchronized (CombinedConfigurationBuilder.this) 1306 { 1307 reset(); 1308 definitionBuilder = defBuilder; 1309 } 1310 } 1311 }); 1312 } 1313 1314 /** 1315 * Returns a map with the current prefix lookup objects. This map is 1316 * obtained from the {@code ConfigurationInterpolator} of the configuration 1317 * under construction. 1318 * 1319 * @return the map with current prefix lookups (may be <b>null</b>) 1320 */ 1321 private Map<String, ? extends Lookup> fetchPrefixLookups() 1322 { 1323 final CombinedConfiguration cc = getConfigurationUnderConstruction(); 1324 return (cc != null) ? cc.getInterpolator().getLookups() : null; 1325 } 1326 1327 /** 1328 * Creates {@code ConfigurationDeclaration} objects for the specified 1329 * configurations. 1330 * 1331 * @param configs the list with configurations 1332 * @return a collection with corresponding declarations 1333 */ 1334 private Collection<ConfigurationDeclaration> createDeclarations( 1335 final Collection<? extends HierarchicalConfiguration<?>> configs) 1336 { 1337 final Collection<ConfigurationDeclaration> declarations = 1338 new ArrayList<>(configs.size()); 1339 for (final HierarchicalConfiguration<?> c : configs) 1340 { 1341 declarations.add(new ConfigurationDeclaration(this, c)); 1342 } 1343 return declarations; 1344 } 1345 1346 /** 1347 * Initializes the list nodes of the node combiner for the given combined 1348 * configuration. This information can be set in the header section of the 1349 * configuration definition file for both the override and the union 1350 * combiners. 1351 * 1352 * @param cc the combined configuration to initialize 1353 * @param defConfig the definition configuration 1354 * @param key the key for the list nodes 1355 */ 1356 private static void initNodeCombinerListNodes(final CombinedConfiguration cc, 1357 final HierarchicalConfiguration<?> defConfig, final String key) 1358 { 1359 final List<Object> listNodes = defConfig.getList(key); 1360 for (final Object listNode : listNodes) 1361 { 1362 cc.getNodeCombiner().addListNode((String) listNode); 1363 } 1364 } 1365 1366 /** 1367 * Creates the map with the default configuration builder providers. 1368 * 1369 * @return the map with default providers 1370 */ 1371 private static Map<String, ConfigurationBuilderProvider> createDefaultProviders() 1372 { 1373 final Map<String, ConfigurationBuilderProvider> providers = 1374 new HashMap<>(); 1375 for (int i = 0; i < DEFAULT_TAGS.length; i++) 1376 { 1377 providers.put(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]); 1378 } 1379 return providers; 1380 } 1381 1382 static 1383 { 1384 DEFAULT_PROVIDERS_MAP = createDefaultProviders(); 1385 } 1386 1387 /** 1388 * A data class for storing information about all configuration sources 1389 * defined for a combined builder. 1390 */ 1391 private class ConfigurationSourceData 1392 { 1393 /** A list with data for all builders for override configurations. */ 1394 private final List<ConfigurationDeclaration> overrideDeclarations; 1395 1396 /** A list with data for all builders for union configurations. */ 1397 private final List<ConfigurationDeclaration> unionDeclarations; 1398 1399 /** A list with the builders for override configurations. */ 1400 private final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders; 1401 1402 /** A list with the builders for union configurations. */ 1403 private final List<ConfigurationBuilder<? extends Configuration>> unionBuilders; 1404 1405 /** A map for direct access to a builder by its name. */ 1406 private final Map<String, ConfigurationBuilder<? extends Configuration>> namedBuilders; 1407 1408 /** A collection with all child builders. */ 1409 private final Collection<ConfigurationBuilder<? extends Configuration>> allBuilders; 1410 1411 /** A listener for reacting on changes of sub builders. */ 1412 private final EventListener<ConfigurationBuilderEvent> changeListener; 1413 1414 /** 1415 * Creates a new instance of {@code ConfigurationSourceData}. 1416 */ 1417 public ConfigurationSourceData() 1418 { 1419 overrideDeclarations = new ArrayList<>(); 1420 unionDeclarations = new ArrayList<>(); 1421 overrideBuilders = new ArrayList<>(); 1422 unionBuilders = new ArrayList<>(); 1423 namedBuilders = new HashMap<>(); 1424 allBuilders = new LinkedList<>(); 1425 changeListener = createBuilderChangeListener(); 1426 } 1427 1428 /** 1429 * Initializes this object from the specified definition configuration. 1430 * 1431 * @param config the definition configuration 1432 * @throws ConfigurationException if an error occurs 1433 */ 1434 public void initFromDefinitionConfiguration( 1435 final HierarchicalConfiguration<?> config) throws ConfigurationException 1436 { 1437 overrideDeclarations.addAll(createDeclarations(fetchTopLevelOverrideConfigs(config))); 1438 overrideDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_OVERRIDE))); 1439 unionDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_UNION))); 1440 } 1441 1442 /** 1443 * Processes the declaration of configuration builder providers, creates 1444 * the corresponding builder if necessary, obtains configurations, and 1445 * adds them to the specified result configuration. 1446 * 1447 * @param ccResult the result configuration 1448 * @param srcDecl the collection with the declarations of configuration 1449 * sources to process 1450 * @return a list with configuration builders 1451 * @throws ConfigurationException if an error occurs 1452 */ 1453 public List<ConfigurationBuilder<? extends Configuration>> createAndAddConfigurations( 1454 final CombinedConfiguration ccResult, 1455 final List<ConfigurationDeclaration> srcDecl, 1456 final List<ConfigurationBuilder<? extends Configuration>> builders) 1457 throws ConfigurationException 1458 { 1459 final boolean createBuilders = builders.isEmpty(); 1460 List<ConfigurationBuilder<? extends Configuration>> newBuilders; 1461 if (createBuilders) 1462 { 1463 newBuilders = new ArrayList<>(srcDecl.size()); 1464 } 1465 else 1466 { 1467 newBuilders = builders; 1468 } 1469 1470 for (int i = 0; i < srcDecl.size(); i++) 1471 { 1472 ConfigurationBuilder<? extends Configuration> b; 1473 if (createBuilders) 1474 { 1475 b = createConfigurationBuilder(srcDecl.get(i)); 1476 newBuilders.add(b); 1477 } 1478 else 1479 { 1480 b = builders.get(i); 1481 } 1482 addChildConfiguration(ccResult, srcDecl.get(i), b); 1483 } 1484 1485 return newBuilders; 1486 } 1487 1488 /** 1489 * Frees resources used by this object and performs clean up. This 1490 * method is called when the owning builder is reset. 1491 */ 1492 public void cleanUp() 1493 { 1494 for (final ConfigurationBuilder<?> b : getChildBuilders()) 1495 { 1496 b.removeEventListener(ConfigurationBuilderEvent.RESET, 1497 changeListener); 1498 } 1499 namedBuilders.clear(); 1500 } 1501 1502 /** 1503 * Returns a collection containing the builders for all child 1504 * configuration sources. 1505 * 1506 * @return the child configuration builders 1507 */ 1508 public Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders() 1509 { 1510 return allBuilders; 1511 } 1512 1513 /** 1514 * Returns a collection with all configuration source declarations 1515 * defined in the override section. 1516 * 1517 * @return the override configuration builders 1518 */ 1519 public List<ConfigurationDeclaration> getOverrideSources() 1520 { 1521 return overrideDeclarations; 1522 } 1523 1524 /** 1525 * Returns a collection with all configuration source declarations 1526 * defined in the union section. 1527 * 1528 * @return the union configuration builders 1529 */ 1530 public List<ConfigurationDeclaration> getUnionSources() 1531 { 1532 return unionDeclarations; 1533 } 1534 1535 /** 1536 * Returns the {@code ConfigurationBuilder} with the given name. If no 1537 * such builder is defined in the definition configuration, result is 1538 * <b>null</b>. 1539 * 1540 * @param name the name of the builder in question 1541 * @return the builder with this name or <b>null</b> 1542 */ 1543 public ConfigurationBuilder<? extends Configuration> getNamedBuilder( 1544 final String name) 1545 { 1546 return namedBuilders.get(name); 1547 } 1548 1549 /** 1550 * Returns a set with the names of all known named builders. 1551 * 1552 * @return the names of the available sub builders 1553 */ 1554 public Set<String> builderNames() 1555 { 1556 return namedBuilders.keySet(); 1557 } 1558 1559 /** 1560 * Creates a configuration builder based on a source declaration in the 1561 * definition configuration. 1562 * 1563 * @param decl the current {@code ConfigurationDeclaration} 1564 * @return the newly created builder 1565 * @throws ConfigurationException if an error occurs 1566 */ 1567 private ConfigurationBuilder<? extends Configuration> createConfigurationBuilder( 1568 final ConfigurationDeclaration decl) throws ConfigurationException 1569 { 1570 final ConfigurationBuilderProvider provider = 1571 providerForTag(decl.getConfiguration().getRootElementName()); 1572 if (provider == null) 1573 { 1574 throw new ConfigurationException( 1575 "Unsupported configuration source: " 1576 + decl.getConfiguration().getRootElementName()); 1577 } 1578 1579 final ConfigurationBuilder<? extends Configuration> builder = 1580 provider.getConfigurationBuilder(decl); 1581 if (decl.getName() != null) 1582 { 1583 namedBuilders.put(decl.getName(), builder); 1584 } 1585 allBuilders.add(builder); 1586 builder.addEventListener(ConfigurationBuilderEvent.RESET, 1587 changeListener); 1588 return builder; 1589 } 1590 1591 /** 1592 * Creates a new configuration using the specified builder and adds it 1593 * to the resulting combined configuration. 1594 * 1595 * @param ccResult the resulting combined configuration 1596 * @param decl the current {@code ConfigurationDeclaration} 1597 * @param builder the configuration builder 1598 * @throws ConfigurationException if an error occurs 1599 */ 1600 private void addChildConfiguration(final CombinedConfiguration ccResult, 1601 final ConfigurationDeclaration decl, 1602 final ConfigurationBuilder<? extends Configuration> builder) 1603 throws ConfigurationException 1604 { 1605 try 1606 { 1607 ccResult.addConfiguration( 1608 builder.getConfiguration(), 1609 decl.getName(), decl.getAt()); 1610 } 1611 catch (final ConfigurationException cex) 1612 { 1613 // ignore exceptions for optional configurations 1614 if (!decl.isOptional()) 1615 { 1616 throw cex; 1617 } 1618 } 1619 } 1620 1621 /** 1622 * Creates a listener for builder change events. This listener is 1623 * registered at all builders for child configurations. 1624 */ 1625 private EventListener<ConfigurationBuilderEvent> createBuilderChangeListener() 1626 { 1627 return new EventListener<ConfigurationBuilderEvent>() 1628 { 1629 @Override 1630 public void onEvent(final ConfigurationBuilderEvent event) 1631 { 1632 resetResult(); 1633 } 1634 }; 1635 } 1636 1637 /** 1638 * Finds the override configurations that are defined as top level 1639 * elements in the configuration definition file. This method fetches 1640 * the child elements of the root node and removes the nodes that 1641 * represent other configuration sections. The remaining nodes are 1642 * treated as definitions for override configurations. 1643 * 1644 * @param config the definition configuration 1645 * @return a list with sub configurations for the top level override 1646 * configurations 1647 */ 1648 private List<? extends HierarchicalConfiguration<?>> fetchTopLevelOverrideConfigs( 1649 final HierarchicalConfiguration<?> config) 1650 { 1651 1652 final List<? extends HierarchicalConfiguration<?>> configs = 1653 config.childConfigurationsAt(null); 1654 for (final Iterator<? extends HierarchicalConfiguration<?>> it = 1655 configs.iterator(); it.hasNext();) 1656 { 1657 final String nodeName = it.next().getRootElementName(); 1658 for (final String element : CONFIG_SECTIONS) 1659 { 1660 if (element.equals(nodeName)) 1661 { 1662 it.remove(); 1663 break; 1664 } 1665 } 1666 } 1667 return configs; 1668 } 1669 } 1670}