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