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