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