View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.builder.combined;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertNotSame;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertSame;
26  import static org.junit.jupiter.api.Assertions.assertThrows;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  import static org.junit.jupiter.api.Assertions.fail;
29  import static org.mockito.Mockito.mock;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.net.URL;
34  import java.util.Arrays;
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.HashMap;
38  import java.util.HashSet;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Set;
42  import java.util.concurrent.CountDownLatch;
43  
44  import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
45  import org.apache.commons.configuration2.CombinedConfiguration;
46  import org.apache.commons.configuration2.Configuration;
47  import org.apache.commons.configuration2.ConfigurationAssert;
48  import org.apache.commons.configuration2.ConfigurationDecoder;
49  import org.apache.commons.configuration2.DynamicCombinedConfiguration;
50  import org.apache.commons.configuration2.HierarchicalConfiguration;
51  import org.apache.commons.configuration2.PropertiesConfiguration;
52  import org.apache.commons.configuration2.XMLConfiguration;
53  import org.apache.commons.configuration2.XMLPropertiesConfiguration;
54  import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
55  import org.apache.commons.configuration2.builder.BuilderEventListenerImpl;
56  import org.apache.commons.configuration2.builder.ConfigurationBuilder;
57  import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
58  import org.apache.commons.configuration2.builder.CopyObjectDefaultHandler;
59  import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
60  import org.apache.commons.configuration2.builder.FileBasedBuilderProperties;
61  import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
62  import org.apache.commons.configuration2.builder.PropertiesBuilderParametersImpl;
63  import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
64  import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
65  import org.apache.commons.configuration2.builder.XMLBuilderProperties;
66  import org.apache.commons.configuration2.builder.fluent.CombinedBuilderParameters;
67  import org.apache.commons.configuration2.builder.fluent.FileBasedBuilderParameters;
68  import org.apache.commons.configuration2.builder.fluent.Parameters;
69  import org.apache.commons.configuration2.builder.fluent.XMLBuilderParameters;
70  import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
71  import org.apache.commons.configuration2.convert.ListDelimiterHandler;
72  import org.apache.commons.configuration2.event.ConfigurationEvent;
73  import org.apache.commons.configuration2.event.Event;
74  import org.apache.commons.configuration2.event.EventListener;
75  import org.apache.commons.configuration2.ex.ConfigurationException;
76  import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
77  import org.apache.commons.configuration2.interpol.Lookup;
78  import org.apache.commons.configuration2.io.DefaultFileSystem;
79  import org.apache.commons.configuration2.io.FileHandler;
80  import org.apache.commons.configuration2.io.FileLocatorUtils;
81  import org.apache.commons.configuration2.io.FileSystem;
82  import org.apache.commons.configuration2.reloading.ReloadingController;
83  import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
84  import org.apache.commons.configuration2.resolver.CatalogResolver;
85  import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
86  import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
87  import org.apache.commons.configuration2.tree.ImmutableNode;
88  import org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine;
89  import org.junit.jupiter.api.AfterEach;
90  import org.junit.jupiter.api.BeforeEach;
91  import org.junit.jupiter.api.Test;
92  
93  /**
94   * Test class for {@code CombinedConfigurationBuilder}.
95   */
96  public class TestCombinedConfigurationBuilder {
97  
98      /**
99       * A test builder provider implementation for testing whether providers can be defined in the definition file.
100      */
101     public static class BuilderProviderTestImpl implements ConfigurationBuilderProvider {
102 
103         /** The test property key of the configuration to be created. */
104         private String propertyKey;
105 
106         @Override
107         public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(final ConfigurationDeclaration decl) throws ConfigurationException {
108             final BaseHierarchicalConfiguration config = new BaseHierarchicalConfiguration();
109             config.addProperty(getPropertyKey(), Boolean.TRUE);
110             return new ConstantConfigurationBuilder(config);
111         }
112 
113         public String getPropertyKey() {
114             return propertyKey;
115         }
116 
117         public void setPropertyKey(final String propertyKey) {
118             this.propertyKey = propertyKey;
119         }
120     }
121 
122     /**
123      * A test combined configuration class for testing whether a specific result configuration class can be declared in the
124      * definition configuration.
125      */
126     public static class CombinedConfigurationTestImpl extends CombinedConfiguration {
127     }
128 
129     /**
130      * A test builder class which always returns the same configuration.
131      */
132     private static final class ConstantConfigurationBuilder extends BasicConfigurationBuilder<BaseHierarchicalConfiguration> {
133         private final BaseHierarchicalConfiguration configuration;
134 
135         public ConstantConfigurationBuilder(final BaseHierarchicalConfiguration conf) {
136             super(BaseHierarchicalConfiguration.class);
137             configuration = conf;
138         }
139 
140         @Override
141         public BaseHierarchicalConfiguration getConfiguration() throws ConfigurationException {
142             return configuration;
143         }
144     }
145 
146     /**
147      * A specialized entity resolver implementation for testing whether properties of a catalog resolver are correctly set.
148      */
149     public static class EntityResolverWithPropertiesTestImpl extends CatalogResolver {
150 
151         /** The base directory. */
152         private String baseDirectory;
153 
154         /** The file system. */
155         private FileSystem fileSystem;
156 
157         /** The ConfigurationInterpolator. */
158         private ConfigurationInterpolator interpolator;
159 
160         public String getBaseDir() {
161             return baseDirectory;
162         }
163 
164         public FileSystem getFileSystem() {
165             return fileSystem;
166         }
167 
168         public ConfigurationInterpolator getInterpolator() {
169             return interpolator;
170         }
171 
172         @Override
173         public void setBaseDir(final String baseDir) {
174             super.setBaseDir(baseDir);
175             baseDirectory = baseDir;
176         }
177 
178         @Override
179         public void setFileSystem(final FileSystem fileSystem) {
180             super.setFileSystem(fileSystem);
181             this.fileSystem = fileSystem;
182         }
183 
184         @Override
185         public void setInterpolator(final ConfigurationInterpolator interpolator) {
186             super.setInterpolator(interpolator);
187             this.interpolator = interpolator;
188         }
189     }
190 
191     /**
192      * A test file system implementation for testing whether a custom file system class can be specified in the
193      * configuration definition file.
194      */
195     public static class FileSystemTestImpl extends DefaultFileSystem {
196     }
197 
198     /**
199      * A thread class for testing concurrent read access to a newly created configuration.
200      */
201     private static final class ReadThread extends Thread {
202 
203         /** The configuration to access. */
204         private final CombinedConfiguration config;
205 
206         /** The start latch. */
207         private final CountDownLatch startLatch;
208 
209         /** The value read from the configuration. */
210         private Boolean value;
211 
212         public ReadThread(final CombinedConfiguration cc, final CountDownLatch latch) {
213             config = cc;
214             startLatch = latch;
215         }
216 
217         @Override
218         public void run() {
219             try {
220                 startLatch.await();
221                 value = config.getBoolean("configuration.loaded");
222             } catch (final InterruptedException iex) {
223                 // ignore
224             }
225         }
226 
227         /**
228          * Verifies that the correct value was read.
229          */
230         public void verify() {
231             try {
232                 join();
233             } catch (final InterruptedException iex) {
234                 fail("Waiting was interrupted: " + iex);
235             }
236             assertEquals(Boolean.TRUE, value);
237         }
238     }
239 
240     /**
241      * A custom Lookup implementation for testing whether lookups can be defined in the definition configuration. This
242      * lookup supports some variables referencing test files.
243      */
244     public static class TestLookup implements Lookup {
245         private final Map<String, String> map = new HashMap<>();
246 
247         public TestLookup() {
248             map.put("test_file_xml", "test.xml");
249             map.put("test_file_combine", "testcombine1.xml");
250             map.put("test_key", "test.value");
251         }
252 
253         @Override
254         public String lookup(final String key) {
255             return map.get(key);
256         }
257     }
258 
259     /** Test configuration definition file. */
260     private static final File TEST_FILE = ConfigurationAssert.getTestFile("testDigesterConfiguration.xml");
261 
262     /** Test file name for a sub configuration. */
263     private static final String TEST_SUB_XML = "test.xml";
264 
265     /** Constant for a named builder. */
266     private static final String BUILDER_NAME = "subBuilderName";
267 
268     /**
269      * The name of the system property for selecting a file managed by a MultiFileConfigurationBuilder.
270      */
271     private static final String MULTI_FILE_PROPERTY = "Id";
272 
273     /**
274      * Helper method for testing the attributes of a combined configuration created by the builder.
275      *
276      * @param cc the configuration to be checked
277      */
278     private static void checkCombinedConfigAttrs(final CombinedConfiguration cc) {
279         final ListDelimiterHandler handler = cc.getListDelimiterHandler();
280         assertInstanceOf(DefaultListDelimiterHandler.class, handler);
281         assertEquals(',', ((DefaultListDelimiterHandler) handler).getDelimiter());
282     }
283 
284     /**
285      * Creates a configuration builder for the definition configuration which always returns the passed in definition
286      * configuration.
287      *
288      * @param defConfig the definition configuration
289      * @return the definition builder
290      */
291     protected static BasicConfigurationBuilder<? extends BaseHierarchicalConfiguration> createDefinitionBuilder(final BaseHierarchicalConfiguration defConfig) {
292         return new ConstantConfigurationBuilder(defConfig);
293     }
294 
295     /**
296      * Convenience method for creating a definition configuration. This method creates a configuration containing a tag
297      * referring to a configuration source. The tag has attributes defined by the given map.
298      *
299      * @param tag the name of the tag to create
300      * @param attrs the attributes of this tag
301      * @return the definition configuration
302      */
303     protected static BaseHierarchicalConfiguration createDefinitionConfig(final String tag, final Map<String, Object> attrs) {
304         final BaseHierarchicalConfiguration defConfig = new BaseHierarchicalConfiguration();
305         final String prefix = "override." + tag;
306         for (final Map.Entry<String, Object> e : attrs.entrySet()) {
307             defConfig.addProperty(prefix + "[@" + e.getKey() + "]", e.getValue());
308         }
309         return defConfig;
310     }
311 
312     /**
313      * Prepares a parameters object for a test for properties inheritance.
314      *
315      * @param params the {@code Parameters} object
316      * @return the builder parameters
317      */
318     private static XMLBuilderParameters prepareParamsForInheritanceTest(final Parameters params) {
319         final DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS)
320             .setPropertyDelimiter("/").create();
321         final DefaultExpressionEngine engine = new DefaultExpressionEngine(symbols);
322         final DefaultListDelimiterHandler listDelimiterHandler = new DefaultListDelimiterHandler(',');
323         return params.xml().setExpressionEngine(engine).setListDelimiterHandler(listDelimiterHandler).setFile(TEST_FILE);
324     }
325 
326     /**
327      * Sets the system property which selects a specific file managed by a MultiFileConfigurationBuilder.
328      *
329      * @param key the key to select the desired file
330      */
331     private static void switchToMultiFile(final String key) {
332         System.setProperty(MULTI_FILE_PROPERTY, key);
333     }
334 
335     /** A helper object for creating builder parameters. */
336     protected Parameters parameters;
337 
338     /** Stores the object to be tested. */
339     protected CombinedConfigurationBuilder builder;
340 
341     /**
342      * Tests if the configuration was correctly created by the builder.
343      *
344      * @return the combined configuration obtained from the builder
345      */
346     private CombinedConfiguration checkConfiguration() throws ConfigurationException {
347         final CombinedConfiguration compositeConfiguration = builder.getConfiguration();
348 
349         assertEquals(3, compositeConfiguration.getNumberOfConfigurations());
350         assertEquals(PropertiesConfiguration.class, compositeConfiguration.getConfiguration(0).getClass());
351         assertEquals(XMLPropertiesConfiguration.class, compositeConfiguration.getConfiguration(1).getClass());
352         assertEquals(XMLConfiguration.class, compositeConfiguration.getConfiguration(2).getClass());
353 
354         // check the first configuration
355         final PropertiesConfiguration pc = (PropertiesConfiguration) compositeConfiguration.getConfiguration(0);
356         assertNotNull(pc);
357 
358         // check some properties
359         checkProperties(compositeConfiguration);
360         return compositeConfiguration;
361     }
362 
363     /**
364      * Helper method for testing whether the file system can be customized in the configuration definition file.
365      *
366      * @param fsFile the file to be processed
367      * @throws ConfigurationException if an error occurs
368      */
369     private void checkFileSystem(final File fsFile) throws ConfigurationException {
370         builder.configure(createParameters().setFile(fsFile));
371         builder.getConfiguration();
372         final FileBasedConfigurationBuilder<? extends Configuration> xmlBuilder = (FileBasedConfigurationBuilder<? extends Configuration>) builder
373             .getNamedBuilder("xml");
374         assertInstanceOf(FileSystemTestImpl.class, xmlBuilder.getFileHandler().getFileSystem());
375     }
376 
377     /**
378      * Helper method for testing whether properties of a MultiFileConfiguration can be obtained.
379      *
380      * @param key the key of the file to be accessed
381      * @param config the configuration to check
382      * @param rows the expected value of the test property
383      */
384     private void checkMultiFile(final String key, final CombinedConfiguration config, final int rows) {
385         switchToMultiFile(key);
386         assertEquals(rows, config.getInt("rowsPerPage"));
387     }
388 
389     /**
390      * Checks if the passed in configuration contains the expected properties.
391      *
392      * @param compositeConfiguration the configuration to check
393      */
394     private void checkProperties(final Configuration compositeConfiguration) {
395         assertTrue(compositeConfiguration.getBoolean("test.boolean"));
396         assertEquals("I'm complex!", compositeConfiguration.getProperty("element2.subelement.subsubelement"));
397         assertEquals("value1", compositeConfiguration.getProperty("key1"));
398     }
399 
400     /**
401      * Loads a test file which includes a MultiFileConfigurationBuilder declaration and returns the resulting configuration.
402      *
403      * @param fileName the name of the file to be loaded
404      * @return the resulting combined configuration
405      * @throws ConfigurationException if an error occurs
406      */
407     private CombinedConfiguration createMultiFileConfig(final String fileName) throws ConfigurationException {
408         final File testFile = ConfigurationAssert.getTestFile(fileName);
409         builder.configure(createParameters().setFile(testFile));
410         final CombinedConfiguration config = builder.getConfiguration();
411         assertInstanceOf(DynamicCombinedConfiguration.class, config);
412         return config;
413     }
414 
415     /**
416      * Creates an object with parameters for defining the file to be loaded.
417      *
418      * @return the parameters object
419      */
420     protected FileBasedBuilderParameters createParameters() {
421         return parameters.fileBased();
422     }
423 
424     /**
425      * Prepares a test with a combined configuration that uses a single sub builder. This method adds some default
426      * attributes to the given map, creates the corresponding definition builder and configures the combined builder.
427      *
428      * @param attrs the map with attributes
429      * @return the definition builder
430      */
431     private BasicConfigurationBuilder<? extends HierarchicalConfiguration<ImmutableNode>> prepareSubBuilderTest(final Map<String, Object> attrs) {
432         attrs.put("fileName", TEST_SUB_XML);
433         attrs.put("config-name", BUILDER_NAME);
434         final BaseHierarchicalConfiguration defConfig = createDefinitionConfig("xml", attrs);
435         final BasicConfigurationBuilder<? extends HierarchicalConfiguration<ImmutableNode>> defBuilder = createDefinitionBuilder(defConfig);
436         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilder(defBuilder));
437         return defBuilder;
438     }
439 
440     @BeforeEach
441     public void setUp() throws Exception {
442         System.setProperty("java.naming.factory.initial", "org.apache.commons.configuration2.MockInitialContextFactory");
443         System.setProperty("test_file_xml", TEST_SUB_XML);
444         System.setProperty("test_file_combine", "testcombine1.xml");
445         parameters = new Parameters();
446         builder = new CombinedConfigurationBuilder();
447     }
448 
449     @AfterEach
450     public void tearDown() throws Exception {
451         System.getProperties().remove(MULTI_FILE_PROPERTY);
452     }
453 
454     /**
455      * Tests if the base path is correctly evaluated.
456      */
457     @Test
458     void testBasePathForChildConfigurations() throws ConfigurationException {
459         final BaseHierarchicalConfiguration defConfig = new BaseHierarchicalConfiguration();
460         defConfig.addProperty("properties[@fileName]", "test.properties");
461         final File deepDir = new File(ConfigurationAssert.TEST_DIR, "config/deep");
462         builder.configure(
463             new CombinedBuilderParametersImpl().setBasePath(deepDir.getAbsolutePath()).setDefinitionBuilder(new ConstantConfigurationBuilder(defConfig)));
464         final CombinedConfiguration config = builder.getConfiguration();
465         assertEquals("somevalue", config.getString("somekey"));
466     }
467 
468     /**
469      * Tests whether the names of sub builders can be queried.
470      */
471     @Test
472     void testBuilderNames() throws ConfigurationException {
473         builder.configure(createParameters().setFile(TEST_FILE));
474         builder.getConfiguration();
475         final Set<String> names = builder.builderNames();
476         assertEquals(new HashSet<>(Arrays.asList("props", "xml")), names);
477     }
478 
479     /**
480      * Tests the behavior of builderNames() before the result configuration has been created.
481      */
482     @Test
483     void testBuilderNamesBeforeConfigurationAccess() {
484         assertEquals(Collections.emptySet(), builder.builderNames());
485         builder.configure(createParameters().setFile(TEST_FILE));
486         assertEquals(Collections.emptySet(), builder.builderNames());
487     }
488 
489     /**
490      * Tests that the collection with builder names cannot be manipulated.
491      */
492     @Test
493     void testBuilderNamesManipulate() throws ConfigurationException {
494         builder.configure(createParameters().setFile(TEST_FILE));
495         builder.getConfiguration();
496         final Set<String> names = builder.builderNames();
497         assertThrows(UnsupportedOperationException.class, () -> names.add(BUILDER_NAME));
498     }
499 
500     /**
501      * Tests that child configuration builders are not initialized multiple times. This test is releated to
502      * CONFIGURATION-687.
503      */
504     @Test
505     void testChildBuildersAreInitializedOnlyOnce() throws ConfigurationException {
506         builder.configure(createParameters().setFile(TEST_FILE));
507         builder.getConfiguration();
508         builder.resetResult();
509         builder.getConfiguration();
510         final Collection<ConfigurationBuilder<? extends Configuration>> childBuilders = builder.getChildBuilders();
511         assertEquals(3, childBuilders.size());
512     }
513 
514     /**
515      * Tests whether attributes are correctly set on the combined configurations for the override and additional sections.
516      */
517     @Test
518     void testCombinedConfigurationAttributes() throws ConfigurationException {
519         final File initFile = ConfigurationAssert.getTestFile("testCCResultInitialization.xml");
520         builder.configure(createParameters().setFile(initFile));
521         final CombinedConfiguration cc = builder.getConfiguration();
522         checkCombinedConfigAttrs(cc);
523         final CombinedConfiguration cc2 = (CombinedConfiguration) cc.getConfiguration(CombinedConfigurationBuilder.ADDITIONAL_NAME);
524         checkCombinedConfigAttrs(cc2);
525     }
526 
527     /**
528      * Tests whether the list node definition was correctly processed.
529      */
530     @Test
531     void testCombinedConfigurationListNodes() throws ConfigurationException {
532         final File initFile = ConfigurationAssert.getTestFile("testCCResultInitialization.xml");
533         builder.configure(createParameters().setFile(initFile));
534         final CombinedConfiguration cc = builder.getConfiguration();
535         Set<String> listNodes = cc.getNodeCombiner().getListNodes();
536         assertEquals(new HashSet<>(Arrays.asList("table", "list")), listNodes);
537 
538         final CombinedConfiguration cca = (CombinedConfiguration) cc.getConfiguration(CombinedConfigurationBuilder.ADDITIONAL_NAME);
539         listNodes = cca.getNodeCombiner().getListNodes();
540         assertEquals(Collections.emptySet(), listNodes);
541     }
542 
543     /**
544      * Tests the structure of the returned combined configuration if there is no additional section.
545      */
546     @Test
547     void testCombinedConfigurationNoAdditional() throws ConfigurationException {
548         builder.configure(createParameters().setFile(TEST_FILE));
549         final CombinedConfiguration cc = builder.getConfiguration();
550         assertNull(cc.getConfiguration(CombinedConfigurationBuilder.ADDITIONAL_NAME));
551     }
552 
553     /**
554      * Tests whether a newly created instance can be read concurrently without a special synchronizer.
555      */
556     @Test
557     void testConcurrentReadAccessWithoutSynchronizer() throws ConfigurationException {
558         builder.configure(createParameters().setFile(TEST_FILE));
559         final CombinedConfiguration config = builder.getConfiguration();
560         final int threadCount = 32;
561         final CountDownLatch startLatch = new CountDownLatch(1);
562         final ReadThread[] threads = new ReadThread[threadCount];
563         for (int i = 0; i < threadCount; i++) {
564             threads[i] = new ReadThread(config, startLatch);
565             threads[i].start();
566         }
567 
568         startLatch.countDown();
569         for (final ReadThread t : threads) {
570             t.verify();
571         }
572     }
573 
574     /**
575      * Tests whether a configuration builder can itself be declared in a configuration definition file.
576      */
577     @Test
578     void testConfigurationBuilderProvider() throws ConfigurationException {
579         final BaseHierarchicalConfiguration defConfig = new BaseHierarchicalConfiguration();
580         defConfig.addProperty("override.configuration[@fileName]", TEST_FILE.getAbsolutePath());
581         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilder(new ConstantConfigurationBuilder(defConfig)));
582         final CombinedConfiguration cc = builder.getConfiguration();
583         assertEquals(1, cc.getNumberOfConfigurations());
584         checkProperties(cc);
585     }
586 
587     /**
588      * Tests whether the base path can be inherited to child combined configuration builders.
589      */
590     @Test
591     void testConfigurationBuilderProviderInheritBasePath() throws ConfigurationException {
592         final File envFile = ConfigurationAssert.getTestFile("testCCEnvProperties.xml");
593         final String basePath = ConfigurationAssert.OUT_DIR.getAbsolutePath();
594         builder.configure(new CombinedBuilderParametersImpl().setBasePath(basePath).setDefinitionBuilderParameters(createParameters().setFile(envFile)));
595         builder.getConfiguration();
596         final CombinedBuilderParametersImpl params = new CombinedBuilderParametersImpl();
597         builder.initChildBuilderParameters(params);
598         assertEquals(basePath, params.getBasePath());
599     }
600 
601     /**
602      * Tests whether basic properties defined for the combined configuration are inherited by a child combined configuration
603      * builder.
604      */
605     @Test
606     void testConfigurationBuilderProviderInheritBasicProperties() throws ConfigurationException {
607         final File testFile = ConfigurationAssert.getTestFile("testCCCombinedChildBuilder.xml");
608         final ListDelimiterHandler listHandler = new DefaultListDelimiterHandler('*');
609         final ConfigurationDecoder decoder = mock(ConfigurationDecoder.class);
610         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilderParameters(new XMLBuilderParametersImpl().setFile(testFile))
611             .setListDelimiterHandler(listHandler).setConfigurationDecoder(decoder));
612         final CombinedConfiguration cc = builder.getConfiguration();
613         final CombinedConfiguration cc2 = (CombinedConfiguration) cc.getConfiguration("subcc");
614         assertFalse(cc2.isThrowExceptionOnMissing());
615         assertEquals(listHandler, cc2.getListDelimiterHandler());
616         assertEquals(decoder, cc2.getConfigurationDecoder());
617     }
618 
619     /**
620      * Tests whether custom builder providers are inherited to child combined configuration builder providers.
621      */
622     @Test
623     void testConfigurationBuilderProviderInheritCustomProviders() throws ConfigurationException {
624         builder.configure(createParameters().setFile(ConfigurationAssert.getTestFile("testCCCustomProvider.xml")));
625         builder.getConfiguration();
626         final CombinedBuilderParametersImpl ccparams = new CombinedBuilderParametersImpl();
627         builder.initChildBuilderParameters(ccparams);
628         assertNotNull(ccparams.providerForTag("test"));
629     }
630 
631     /**
632      * Tests whether a child configuration builder inherits the event listeners from its parent.
633      */
634     @Test
635     void testConfigurationBuilderProviderInheritEventListeners() throws ConfigurationException {
636         @SuppressWarnings("unchecked")
637         final EventListener<Event> l1 = mock(EventListener.class);
638         @SuppressWarnings("unchecked")
639         final EventListener<ConfigurationEvent> l2 = mock(EventListener.class);
640 
641         final File testFile = ConfigurationAssert.getTestFile("testCCCombinedChildBuilder.xml");
642         builder.configure(new XMLBuilderParametersImpl().setFile(testFile));
643         builder.addEventListener(Event.ANY, l1);
644         builder.addEventListener(ConfigurationEvent.ANY, l2);
645         final CombinedConfiguration cc = builder.getConfiguration();
646         final CombinedConfiguration cc2 = (CombinedConfiguration) cc.getConfiguration("subcc");
647         final Collection<EventListener<? super ConfigurationEvent>> listeners = cc2.getEventListeners(ConfigurationEvent.ANY);
648         assertTrue(listeners.contains(l1));
649         assertTrue(listeners.contains(l2));
650         final Collection<EventListener<? super Event>> eventListeners = cc2.getEventListeners(Event.ANY);
651         assertEquals(1, eventListeners.size());
652         assertTrue(eventListeners.contains(l1));
653     }
654 
655     /**
656      * Tests whether the entity resolver is initialized with other XML-related properties.
657      */
658     @Test
659     void testConfigureEntityResolverWithProperties() throws ConfigurationException {
660         final HierarchicalConfiguration<ImmutableNode> config = new BaseHierarchicalConfiguration();
661         config.addProperty("header.entity-resolver[@config-class]", EntityResolverWithPropertiesTestImpl.class.getName());
662         final XMLBuilderParametersImpl xmlParams = new XMLBuilderParametersImpl();
663         final FileSystem fs = mock(FileSystem.class);
664         final String baseDir = ConfigurationAssert.OUT_DIR_NAME;
665         xmlParams.setBasePath(baseDir);
666         xmlParams.setFileSystem(fs);
667         builder.configureEntityResolver(config, xmlParams);
668         final EntityResolverWithPropertiesTestImpl resolver = (EntityResolverWithPropertiesTestImpl) xmlParams.getEntityResolver();
669         assertSame(fs, resolver.getFileSystem());
670         assertSame(baseDir, resolver.getBaseDir());
671     }
672 
673     /**
674      * Tests that the return value of configure() is overloaded.
675      */
676     @Test
677     void testConfigureResult() {
678         final CombinedConfigurationBuilder configuredBuilder = builder.configure(createParameters().setFile(TEST_FILE));
679         assertSame(builder, configuredBuilder);
680     }
681 
682     /**
683      * Tests whether a custom provider can be registered.
684      */
685     @Test
686     void testCustomBuilderProvider() throws ConfigurationException {
687         final String tagName = "myTestTag";
688         final BaseHierarchicalConfiguration dataConf = new BaseHierarchicalConfiguration();
689         dataConf.addProperty(tagName, Boolean.TRUE);
690         final Map<String, Object> attrs = new HashMap<>();
691         attrs.put("config-name", BUILDER_NAME);
692         attrs.put("config-at", "tests");
693         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilder(createDefinitionBuilder(createDefinitionConfig(tagName, attrs)))
694             .registerProvider(tagName, decl -> new ConstantConfigurationBuilder(dataConf)));
695         final CombinedConfiguration cc = builder.getConfiguration();
696         assertEquals(dataConf, cc.getConfiguration(BUILDER_NAME));
697         assertEquals(Boolean.TRUE, cc.getProperty("tests." + tagName));
698     }
699 
700     /**
701      * Tests whether an entity resolver can be defined in the definition file.
702      */
703     @Test
704     void testCustomEntityResolver() throws ConfigurationException {
705         final File resolverFile = ConfigurationAssert.getTestFile("testCCEntityResolver.xml");
706         builder.configure(createParameters().setFile(resolverFile));
707         final CombinedConfiguration cc = builder.getConfiguration();
708         final XMLConfiguration xmlConf = (XMLConfiguration) cc.getConfiguration("xml");
709         final EntityResolverWithPropertiesTestImpl resolver = (EntityResolverWithPropertiesTestImpl) xmlConf.getEntityResolver();
710         assertFalse(resolver.getInterpolator().getLookups().isEmpty());
711     }
712 
713     /**
714      * Tests whether a default file system can be configured in the definition file.
715      */
716     @Test
717     void testCustomFileSystem() throws ConfigurationException {
718         checkFileSystem(ConfigurationAssert.getTestFile("testCCFileSystem.xml"));
719     }
720 
721     /**
722      * Tests whether a specific file system can be applied to a sub configuration.
723      */
724     @Test
725     void testCustomFileSystemForSubConfig() throws ConfigurationException {
726         checkFileSystem(ConfigurationAssert.getTestFile("testCCFileSystemSubConfig.xml"));
727     }
728 
729     /**
730      * Tests whether a Lookup object can be declared in the definition configuration.
731      */
732     @Test
733     void testCustomLookup() throws ConfigurationException {
734         final File testFile = ConfigurationAssert.getTestFile("testCCLookup.xml");
735         builder.configure(createParameters().setFile(testFile));
736         final CombinedConfiguration cc = builder.getConfiguration();
737         assertTrue(cc.getInterpolator().getLookups().containsKey("test"));
738         final Configuration xmlConf = cc.getConfiguration("xml");
739         assertTrue(xmlConf.getInterpolator().getLookups().containsKey("test"));
740     }
741 
742     /**
743      * Tests whether the resulting combined configuration can be customized.
744      */
745     @Test
746     void testCustomResultConfiguration() throws ConfigurationException {
747         final File testFile = ConfigurationAssert.getTestFile("testCCResultClass.xml");
748         final ListDelimiterHandler listHandler = new DefaultListDelimiterHandler('.');
749         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilderParameters(new XMLBuilderParametersImpl().setFile(testFile))
750             .setListDelimiterHandler(listHandler).setThrowExceptionOnMissing(false));
751         final CombinedConfiguration cc = builder.getConfiguration();
752         assertInstanceOf(CombinedConfigurationTestImpl.class, cc);
753         assertTrue(cc.isThrowExceptionOnMissing());
754         assertEquals(listHandler, cc.getListDelimiterHandler());
755     }
756 
757     /**
758      * Tests whether the default base path for file-based configurations is derived from the configuration definition
759      * builder.
760      */
761     @Test
762     void testDefaultBasePathFromDefinitionBuilder() throws ConfigurationException, IOException {
763         final String testFile = "testCCSystemProperties.xml";
764         builder.configure(new CombinedBuilderParametersImpl()
765             .setDefinitionBuilderParameters(createParameters().setBasePath(ConfigurationAssert.TEST_DIR.getAbsolutePath()).setFileName(testFile)));
766         builder.getConfiguration();
767         final XMLBuilderParametersImpl xmlParams = new XMLBuilderParametersImpl();
768         builder.initChildBuilderParameters(xmlParams);
769         final File basePathFile = FileLocatorUtils.fileFromURL(new URL(xmlParams.getFileHandler().getBasePath()));
770         assertEquals(ConfigurationAssert.getTestFile(testFile).getAbsoluteFile(), basePathFile);
771     }
772 
773     /**
774      * Tests whether a default base path for all file-based child configurations can be set in the builder parameters.
775      */
776     @Test
777     void testDefaultBasePathInParameters() throws ConfigurationException {
778         final File testFile = ConfigurationAssert.getTestFile("testCCSystemProperties.xml");
779         final String basePath = ConfigurationAssert.OUT_DIR.getAbsolutePath();
780         builder.configure(new CombinedBuilderParametersImpl().setBasePath(basePath).setDefinitionBuilderParameters(createParameters().setFile(testFile)));
781         builder.getConfiguration();
782         final XMLBuilderParametersImpl xmlParams = new XMLBuilderParametersImpl();
783         builder.initChildBuilderParameters(xmlParams);
784         assertEquals(basePath, xmlParams.getFileHandler().getBasePath());
785     }
786 
787     /**
788      * Tests whether environment properties can be added as a configuration source.
789      */
790     @Test
791     void testEnvironmentProperties() throws ConfigurationException {
792         final File envFile = ConfigurationAssert.getTestFile("testCCEnvProperties.xml");
793         builder.configure(createParameters().setFile(envFile));
794         final CombinedConfiguration cc = builder.getConfiguration();
795         assertFalse(cc.isEmpty());
796 
797         // The environment may contain settings with values that
798         // are altered by interpolation. Disable this for direct access
799         // to the String associated with the environment property name.
800         cc.setInterpolator(null);
801 
802         // Test the environment is available through the configuration
803         for (final Map.Entry<String, String> e : System.getenv().entrySet()) {
804             assertEquals(e.getValue(), cc.getString(e.getKey()), "Wrong value for property: " + e.getKey());
805         }
806     }
807 
808     /**
809      * Tests whether all child builders can be obtained.
810      */
811     @Test
812     void testGetChildBuilders() throws ConfigurationException {
813         builder.configure(createParameters().setFile(TEST_FILE));
814         builder.getConfiguration();
815         final Collection<ConfigurationBuilder<? extends Configuration>> childBuilders = builder.getChildBuilders();
816         assertEquals(3, childBuilders.size());
817     }
818 
819     /**
820      * Tests whether named builders can be accessed.
821      */
822     @Test
823     void testGetNamedBuilder() throws ConfigurationException {
824         builder.configure(createParameters().setFile(TEST_FILE));
825         builder.getConfiguration();
826         final ConfigurationBuilder<? extends Configuration> propBuilder = builder.getNamedBuilder("props");
827         assertInstanceOf(FileBasedConfigurationBuilder.class, propBuilder);
828         assertInstanceOf(PropertiesConfiguration.class, propBuilder.getConfiguration());
829     }
830 
831     /**
832      * Tries to query a named builder before the result configuration has been created.
833      */
834     @Test
835     void testGetNamedBuilderBeforeConfigurationAccess() {
836         builder.configure(createParameters().setFile(TEST_FILE));
837         assertThrows(ConfigurationException.class, () -> builder.getNamedBuilder("nonExistingBuilder"));
838     }
839 
840     /**
841      * Tries to query a non-existing builder by name.
842      */
843     @Test
844     void testGetNamedBuilderUnknown() throws ConfigurationException {
845         builder.configure(createParameters().setFile(TEST_FILE));
846         builder.getConfiguration();
847         assertThrows(ConfigurationException.class, () -> builder.getNamedBuilder("nonExistingBuilder"));
848     }
849 
850     /**
851      * Tests whether builder properties can be inherited by child builders.
852      */
853     @Test
854     void testInheritProperties() throws ConfigurationException {
855         final Parameters params = new Parameters();
856         final XMLBuilderParameters xmlParams = prepareParamsForInheritanceTest(params);
857         builder.configure(xmlParams);
858         final CombinedConfiguration config = builder.getConfiguration();
859 
860         List<String> list = config.getList(String.class, "test/mixed/array");
861         assertTrue(list.size() > 2);
862         final String[] stringArray = config.getStringArray("test/mixed/array");
863         assertTrue(stringArray.length > 2);
864         final XMLConfiguration xmlConfig = (XMLConfiguration) config.getConfiguration("xml");
865         list = xmlConfig.getList(String.class, "split/list1");
866         assertEquals(3, list.size());
867     }
868 
869     /**
870      * Tests whether an INI configuration source can be added to the combined configuration.
871      */
872     @Test
873     void testINIConfiguration() throws ConfigurationException {
874         final File multiFile = ConfigurationAssert.getTestFile("testDigesterConfiguration3.xml");
875         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilderParameters(createParameters().setFile(multiFile)));
876         final CombinedConfiguration cc = builder.getConfiguration();
877         assertEquals("yes", cc.getString("testini.loaded"));
878     }
879 
880     /**
881      * Tests whether default child properties in the combined builder's configuration are inherited by child parameter
882      * objects.
883      */
884     @Test
885     void testInitChildBuilderParametersDefaultChildProperties() throws ConfigurationException {
886         final Long defRefresh = 60000L;
887         final Long xmlRefresh = 30000L;
888         builder.configure(parameters.combined().setDefinitionBuilderParameters(parameters.fileBased().setFile(TEST_FILE))
889             .registerChildDefaultsHandler(FileBasedBuilderProperties.class,
890                 new CopyObjectDefaultHandler(new FileBasedBuilderParametersImpl().setReloadingRefreshDelay(defRefresh).setThrowExceptionOnMissing(true)))
891             .registerChildDefaultsHandler(XMLBuilderProperties.class, new CopyObjectDefaultHandler(
892                 new XMLBuilderParametersImpl().setValidating(false).setExpressionEngine(new XPathExpressionEngine()).setReloadingRefreshDelay(xmlRefresh))));
893         builder.getConfiguration();
894         final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl();
895         builder.initChildBuilderParameters(params);
896         assertInstanceOf(XPathExpressionEngine.class, params.getParameters().get("expressionEngine"));
897         assertEquals(Boolean.FALSE, params.getParameters().get("validating"));
898         assertEquals(xmlRefresh, params.getReloadingRefreshDelay());
899         assertEquals(Boolean.TRUE, params.getParameters().get("throwExceptionOnMissing"));
900 
901         final PropertiesBuilderParametersImpl params2 = new PropertiesBuilderParametersImpl();
902         builder.initChildBuilderParameters(params2);
903         assertEquals(defRefresh, params2.getReloadingRefreshDelay());
904     }
905 
906     /**
907      * Tests whether variable substitution works across multiple child configurations and also in the definition
908      * configuration.
909      */
910     @Test
911     void testInterpolationOverMultipleSources() throws ConfigurationException {
912         final File testFile = ConfigurationAssert.getTestFile("testInterpolationBuilder.xml");
913         builder.configure(createParameters().setFile(testFile));
914         final CombinedConfiguration combConfig = builder.getConfiguration();
915         assertEquals("abc-product", combConfig.getString("products.product.desc"));
916         final XMLConfiguration xmlConfig = (XMLConfiguration) combConfig.getConfiguration("test");
917         assertEquals("abc-product", xmlConfig.getString("products/product/desc"));
918         final HierarchicalConfiguration<ImmutableNode> subConfig = xmlConfig.configurationAt("products/product[@name='abc']", true);
919         assertEquals("abc-product", subConfig.getString("desc"));
920     }
921 
922     /**
923      * Tests whether a JNDI configuration can be integrated into the combined configuration.
924      */
925     @Test
926     void testJndiConfiguration() throws ConfigurationException {
927         final File multiFile = ConfigurationAssert.getTestFile("testDigesterConfiguration3.xml");
928         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilderParameters(createParameters().setFile(multiFile)));
929         final CombinedConfiguration cc = builder.getConfiguration();
930         assertTrue(cc.getBoolean("test.onlyinjndi"));
931     }
932 
933     /**
934      * Tests loading a configuration definition file with an additional section.
935      */
936     @Test
937     void testLoadAdditional() throws ConfigurationException {
938         final File additonalFile = ConfigurationAssert.getTestFile("testDigesterConfiguration2.xml");
939         builder.configure(createParameters().setFile(additonalFile));
940         final CombinedConfiguration compositeConfiguration = builder.getConfiguration();
941         assertEquals(2, compositeConfiguration.getNumberOfConfigurations());
942 
943         // Test if union was constructed correctly
944         Object prop = compositeConfiguration.getProperty("tables.table.name");
945         Collection<?> collection = assertInstanceOf(Collection.class, prop);
946         assertEquals(3, collection.size());
947         assertEquals("users", compositeConfiguration.getProperty("tables.table(0).name"));
948         assertEquals("documents", compositeConfiguration.getProperty("tables.table(1).name"));
949         assertEquals("tasks", compositeConfiguration.getProperty("tables.table(2).name"));
950 
951         prop = compositeConfiguration.getProperty("tables.table.fields.field.name");
952         collection = assertInstanceOf(Collection.class, prop);
953         assertEquals(17, collection.size());
954 
955         assertEquals("smtp.mydomain.org", compositeConfiguration.getString("mail.host.smtp"));
956         assertEquals("pop3.mydomain.org", compositeConfiguration.getString("mail.host.pop"));
957 
958         // This was overridden
959         assertEquals("masterOfPost", compositeConfiguration.getString("mail.account.user"));
960         assertEquals("topsecret", compositeConfiguration.getString("mail.account.psswd"));
961 
962         // This was overridden, too, but not in additional section
963         assertEquals("enhanced factory", compositeConfiguration.getString("test.configuration"));
964     }
965 
966     /**
967      * Tests loading a simple configuration definition file.
968      */
969     @Test
970     void testLoadConfiguration() throws ConfigurationException {
971         builder.configure(createParameters().setFile(TEST_FILE));
972         checkConfiguration();
973     }
974 
975     /**
976      * Tests loading a definition file that contains optional configurations.
977      */
978     @Test
979     void testLoadOptional() throws Exception {
980         final File optionalFile = ConfigurationAssert.getTestFile("testDigesterOptionalConfiguration.xml");
981         builder.configure(createParameters().setFile(optionalFile));
982         final Configuration config = builder.getConfiguration();
983         assertTrue(config.getBoolean("test.boolean"));
984         assertEquals("value", config.getProperty("element"));
985     }
986 
987     /**
988      * Tests whether the force-create attribute is taken into account.
989      */
990     @Test
991     void testLoadOptionalForceCreate() throws ConfigurationException {
992         final String name = "optionalConfig";
993         final Map<String, Object> attrs = new HashMap<>();
994         attrs.put("fileName", "nonExisting.xml");
995         attrs.put("config-name", name);
996         attrs.put("config-optional", Boolean.TRUE);
997         attrs.put("config-forceCreate", Boolean.TRUE);
998         final BaseHierarchicalConfiguration defConfig = createDefinitionConfig("xml", attrs);
999         final BasicConfigurationBuilder<? extends BaseHierarchicalConfiguration> defBuilder = createDefinitionBuilder(defConfig);
1000         builder.configure(new CombinedBuilderParametersImpl().setDefinitionBuilder(defBuilder));
1001         final CombinedConfiguration cc = builder.getConfiguration();
1002         assertEquals(1, cc.getNumberOfConfigurations());
1003         assertInstanceOf(XMLConfiguration.class, cc.getConfiguration(name));
1004     }
1005 
1006     /**
1007      * Tests loading a definition file with optional and non optional configuration sources. One non optional does not
1008      * exist, so this should cause an exception.
1009      */
1010     @Test
1011     void testLoadOptionalWithException() {
1012         final File optionalExFile = ConfigurationAssert.getTestFile("testDigesterOptionalConfigurationEx.xml");
1013         builder.configure(createParameters().setFile(optionalExFile));
1014         assertThrows(ConfigurationException.class, builder::getConfiguration);
1015     }
1016 
1017     /**
1018      * Tests whether a MultiFileConfigurationBuilder can be integrated into a combined configuration definition.
1019      */
1020     @Test
1021     void testMultiTenentConfiguration() throws ConfigurationException {
1022         final CombinedConfiguration config = createMultiFileConfig("testCCMultiTenent.xml");
1023         checkMultiFile("1001", config, 15);
1024         checkMultiFile("1002", config, 25);
1025         checkMultiFile("1003", config, 35);
1026         checkMultiFile("1004", config, 50);
1027     }
1028 
1029     /**
1030      * Tests whether a configuration created by a MultiFileConfigurationBuilder has been initialized correctly.
1031      */
1032     @Test
1033     void testMultiTenentConfigurationProperties() throws ConfigurationException {
1034         final CombinedConfiguration config = createMultiFileConfig("testCCMultiTenent.xml");
1035         switchToMultiFile("1001");
1036         final HierarchicalConfiguration<?> multiConf = (HierarchicalConfiguration<?>) config.getConfiguration("clientConfig");
1037         assertInstanceOf(XPathExpressionEngine.class, multiConf.getExpressionEngine());
1038         assertEquals("#808080", config.getString("colors.background"));
1039         assertEquals("#000000", multiConf.getString("colors/text"));
1040     }
1041 
1042     /**
1043      * Tests whether reloading support works for MultiFileConfigurationBuilder.
1044      */
1045     @Test
1046     void testMultiTenentConfigurationReloading() throws ConfigurationException, InterruptedException {
1047         final CombinedConfiguration config = createMultiFileConfig("testCCMultiTenentReloading.xml");
1048         final File outFile = ConfigurationAssert.getOutFile("MultiFileReloadingTest.xml");
1049         switchToMultiFile(outFile.getAbsolutePath());
1050         final XMLConfiguration reloadConfig = new XMLConfiguration();
1051         final FileHandler handler = new FileHandler(reloadConfig);
1052         handler.setFile(outFile);
1053         final String key = "test.reload";
1054         reloadConfig.setProperty(key, "no");
1055         handler.save();
1056         try {
1057             assertEquals("no", config.getString(key));
1058             final ConfigurationBuilder<? extends Configuration> childBuilder = builder.getNamedBuilder("clientConfig");
1059             final ReloadingControllerSupport reloadingControllerSupport = assertInstanceOf(ReloadingControllerSupport.class, childBuilder);
1060             final ReloadingController ctrl = reloadingControllerSupport.getReloadingController();
1061             ctrl.checkForReloading(null); // initialize reloading
1062             final BuilderEventListenerImpl listener = new BuilderEventListenerImpl();
1063             childBuilder.addEventListener(ConfigurationBuilderEvent.RESET, listener);
1064             reloadConfig.setProperty(key, "yes");
1065             handler.save();
1066 
1067             int attempts = 10;
1068             boolean changeDetected;
1069             do {
1070                 changeDetected = ctrl.checkForReloading(null);
1071                 if (!changeDetected) {
1072                     Thread.sleep(1000);
1073                     handler.save(outFile);
1074                 }
1075             } while (!changeDetected && --attempts > 0);
1076             assertTrue(changeDetected);
1077             assertEquals("yes", builder.getConfiguration().getString(key));
1078             final ConfigurationBuilderEvent event = listener.nextEvent(ConfigurationBuilderEvent.RESET);
1079             listener.assertNoMoreEvents();
1080             final BasicConfigurationBuilder<?> multiBuilder = (BasicConfigurationBuilder<?>) event.getSource();
1081             childBuilder.removeEventListener(ConfigurationBuilderEvent.RESET, listener);
1082             multiBuilder.resetResult();
1083             listener.assertNoMoreEvents();
1084         } finally {
1085             assertTrue(outFile.delete());
1086         }
1087     }
1088 
1089     /**
1090      * Tries to build a configuration if no definition builder is provided.
1091      */
1092     @Test
1093     void testNoDefinitionBuilder() {
1094         assertThrows(ConfigurationException.class, builder::getConfiguration);
1095     }
1096 
1097     /**
1098      * Tests whether a custom provider can be defined in the definition file.
1099      */
1100     @Test
1101     void testProviderInDefinitionConfig() throws ConfigurationException {
1102         builder.configure(createParameters().setFile(ConfigurationAssert.getTestFile("testCCCustomProvider.xml")));
1103         final CombinedConfiguration cc = builder.getConfiguration();
1104         assertTrue(cc.getBoolean("testKey"));
1105     }
1106 
1107     /**
1108      * Tests whether a reset of one of the sub builders causes the combined configuration to be re-created.
1109      */
1110     @Test
1111     void testReactOnSubBuilderChange() throws ConfigurationException {
1112         final Map<String, Object> attrs = new HashMap<>();
1113         prepareSubBuilderTest(attrs);
1114         final CombinedConfiguration cc = builder.getConfiguration();
1115         final BasicConfigurationBuilder<?> subBuilder = (BasicConfigurationBuilder<?>) builder.getNamedBuilder(BUILDER_NAME);
1116         subBuilder.reset();
1117         assertNotSame(cc, builder.getConfiguration());
1118     }
1119 
1120     /**
1121      * Tests whether a reloading sub builder can be created.
1122      */
1123     @Test
1124     void testReloadingBuilder() throws ConfigurationException {
1125         final Map<String, Object> attrs = new HashMap<>();
1126         attrs.put("config-reload", Boolean.TRUE);
1127         prepareSubBuilderTest(attrs);
1128         builder.getConfiguration();
1129         assertInstanceOf(ReloadingFileBasedConfigurationBuilder.class, builder.getNamedBuilder(BUILDER_NAME));
1130     }
1131 
1132     /**
1133      * Tests that change listeners registered at sub builders are removed on a reset.
1134      */
1135     @Test
1136     void testRemoveSubBuilderListener() throws ConfigurationException {
1137         final Map<String, Object> attrs = new HashMap<>();
1138         prepareSubBuilderTest(attrs);
1139         builder.getConfiguration();
1140         final BasicConfigurationBuilder<?> subBuilder = (BasicConfigurationBuilder<?>) builder.getNamedBuilder(BUILDER_NAME);
1141         builder.reset();
1142         prepareSubBuilderTest(attrs);
1143         final CombinedConfiguration cc = builder.getConfiguration();
1144         final BasicConfigurationBuilder<?> subBuilder2 = (BasicConfigurationBuilder<?>) builder.getNamedBuilder(BUILDER_NAME);
1145         assertNotSame(subBuilder, subBuilder2);
1146         subBuilder.reset();
1147         assertSame(cc, builder.getConfiguration());
1148     }
1149 
1150     /**
1151      * Tests a reset of the builder. The configuration instance should be created anew.
1152      */
1153     @Test
1154     void testResetBuilder() throws ConfigurationException {
1155         final Map<String, Object> attrs = new HashMap<>();
1156         final BasicConfigurationBuilder<? extends HierarchicalConfiguration<ImmutableNode>> defBuilder = prepareSubBuilderTest(attrs);
1157         final CombinedConfiguration cc = builder.getConfiguration();
1158         final ConfigurationBuilder<? extends Configuration> subBuilder = builder.getNamedBuilder(BUILDER_NAME);
1159         defBuilder.reset();
1160         final CombinedConfiguration cc2 = builder.getConfiguration();
1161         assertNotSame(cc, cc2);
1162         final ConfigurationBuilder<? extends Configuration> subBuilder2 = builder.getNamedBuilder(BUILDER_NAME);
1163         assertNotSame(subBuilder, subBuilder2);
1164     }
1165 
1166     /**
1167      * Tests that the combined configuration has been fully constructed (including its root node) when it is returned from
1168      * the builder.
1169      */
1170     @Test
1171     void testRootNodeInitializedAfterCreation() throws ConfigurationException {
1172         builder.configure(createParameters().setFile(TEST_FILE));
1173         final CombinedConfiguration cc = builder.getConfiguration();
1174         assertNotNull(cc.getNodeModel().getNodeHandler().getRootNode());
1175     }
1176 
1177     /**
1178      * Tests whether the inheritance of builder properties can be disabled.
1179      */
1180     @Test
1181     void testSuppressChildBuilderPropertyInheritance() throws ConfigurationException {
1182         final Parameters params = new Parameters();
1183         final CombinedBuilderParameters combinedParams = params.combined().setInheritSettings(false);
1184         builder.configure(combinedParams, prepareParamsForInheritanceTest(params));
1185         final CombinedConfiguration config = builder.getConfiguration();
1186 
1187         final XMLConfiguration xmlConfig = (XMLConfiguration) config.getConfiguration("xml");
1188         final List<String> list = xmlConfig.getList(String.class, "split.list1");
1189         assertEquals(1, list.size());
1190     }
1191 
1192     /**
1193      * Tests whether a file with system properties can be specified in the configuration definition file and that system
1194      * properties can be added to the resulting configuration.
1195      */
1196     @Test
1197     void testSystemProperties() throws ConfigurationException {
1198         final File systemFile = ConfigurationAssert.getTestFile("testCCSystemProperties.xml");
1199         builder.configure(createParameters().setFile(systemFile));
1200         final CombinedConfiguration cc = builder.getConfiguration();
1201         assertTrue(cc.containsKey("user.name"));
1202         assertEquals("value1", System.getProperty("key1"));
1203     }
1204 }