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