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