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  
18  package org.apache.commons.configuration;
19  
20  import java.io.File;
21  import java.io.FileWriter;
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.NoSuchElementException;
29  
30  import junit.framework.TestCase;
31  
32  import org.apache.commons.configuration.event.ConfigurationEvent;
33  import org.apache.commons.configuration.event.ConfigurationListener;
34  import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
35  
36  /***
37   * Test loading multiple configurations.
38   *
39   * @version $Id: TestCompositeConfiguration.java 705028 2008-10-15 20:33:35Z oheger $
40   */
41  public class TestCompositeConfiguration extends TestCase
42  {
43      /*** Constant for a test property to be checked.*/
44      private static final String TEST_PROPERTY = "test.source.property";
45  
46      protected PropertiesConfiguration conf1;
47      protected PropertiesConfiguration conf2;
48      protected XMLConfiguration xmlConf;
49      protected CompositeConfiguration cc;
50  
51      /***
52       * The File that we test with
53       */
54      private String testProperties = new File("conf/test.properties").getAbsolutePath();
55      private String testProperties2 = new File("conf/test2.properties").getAbsolutePath();
56      private String testPropertiesXML = new File("conf/test.xml").getAbsolutePath();
57  
58      protected void setUp() throws Exception
59      {
60          cc = new CompositeConfiguration();
61          conf1 = new PropertiesConfiguration(testProperties);
62          conf2 = new PropertiesConfiguration(testProperties2);
63          xmlConf = new XMLConfiguration(new File(testPropertiesXML));
64  
65          cc.setThrowExceptionOnMissing(true);
66      }
67  
68      public void testThrowExceptionOnMissing()
69      {
70          assertTrue("Throw Exception Property is not set!", cc.isThrowExceptionOnMissing());
71      }
72  
73      public void testAddRemoveConfigurations() throws Exception
74      {
75          cc.addConfiguration(conf1);
76          assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
77          cc.addConfiguration(conf1);
78          assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
79          cc.addConfiguration(conf2);
80          assertEquals("Number of configurations", 3, cc.getNumberOfConfigurations());
81          cc.removeConfiguration(conf1);
82          assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
83          cc.clear();
84          assertEquals("Number of configurations", 1, cc.getNumberOfConfigurations());
85      }
86  
87      public void testGetPropertyWIncludes() throws Exception
88      {
89          cc.addConfiguration(conf1);
90          cc.addConfiguration(conf2);
91          List l = cc.getList("packages");
92          assertTrue(l.contains("packagea"));
93      }
94  
95      public void testGetProperty() throws Exception
96      {
97          cc.addConfiguration(conf1);
98          cc.addConfiguration(conf2);
99          assertEquals("Make sure we get the property from conf1 first", "test.properties", cc.getString("propertyInOrder"));
100         cc.clear();
101 
102         cc.addConfiguration(conf2);
103         cc.addConfiguration(conf1);
104         assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
105     }
106 
107     public void testCantRemoveMemoryConfig() throws Exception
108     {
109         cc.clear();
110         assertEquals(1, cc.getNumberOfConfigurations());
111 
112         Configuration internal = cc.getConfiguration(0);
113         cc.removeConfiguration(internal);
114 
115         assertEquals(1, cc.getNumberOfConfigurations());
116     }
117 
118     public void testGetPropertyMissing() throws Exception
119     {
120         cc.addConfiguration(conf1);
121         cc.addConfiguration(conf2);
122         try
123         {
124             assertNull(cc.getString("bogus.property"));
125             fail("Should have thrown a NoSuchElementException");
126         }
127         catch (NoSuchElementException nsee)
128         {
129             assertTrue(nsee.getMessage().indexOf("bogus.property") > -1);
130         }
131 
132         assertTrue("Should be false", !cc.getBoolean("test.missing.boolean", false));
133         assertTrue("Should be true", cc.getBoolean("test.missing.boolean.true", true));
134     }
135 
136     /***
137      * Tests <code>List</code> parsing.
138      */
139     public void testMultipleTypesOfConfigs() throws Exception
140     {
141         cc.addConfiguration(conf1);
142         cc.addConfiguration(xmlConf);
143         assertEquals("Make sure we get the property from conf1 first", 1, cc.getInt("test.short"));
144         cc.clear();
145 
146         cc.addConfiguration(xmlConf);
147         cc.addConfiguration(conf1);
148         assertEquals("Make sure we get the property from xml", 8, cc.getInt("test.short"));
149     }
150 
151     /***
152      * Tests <code>List</code> parsing.
153      */
154     public void testPropertyExistsInOnlyOneConfig() throws Exception
155     {
156         cc.addConfiguration(conf1);
157         cc.addConfiguration(xmlConf);
158         assertEquals("value", cc.getString("element"));
159     }
160 
161     /***
162      * Tests getting a default when the key doesn't exist
163      */
164     public void testDefaultValueWhenKeyMissing() throws Exception
165     {
166         cc.addConfiguration(conf1);
167         cc.addConfiguration(xmlConf);
168         assertEquals("default", cc.getString("bogus", "default"));
169         assertTrue(1.4 == cc.getDouble("bogus", 1.4));
170         assertTrue(1.4 == cc.getDouble("bogus", 1.4));
171     }
172 
173     /***
174      * Tests <code>List</code> parsing.
175      */
176     public void testGettingConfiguration() throws Exception
177     {
178         cc.addConfiguration(conf1);
179         cc.addConfiguration(xmlConf);
180         assertEquals(PropertiesConfiguration.class, cc.getConfiguration(0).getClass());
181         assertEquals(XMLConfiguration.class, cc.getConfiguration(1).getClass());
182     }
183 
184     /***
185      * Tests setting values.  These are set in memory mode only!
186      */
187     public void testClearingProperty() throws Exception
188     {
189         cc.addConfiguration(conf1);
190         cc.addConfiguration(xmlConf);
191         cc.clearProperty("test.short");
192         assertTrue("Make sure test.short is gone!", !cc.containsKey("test.short"));
193     }
194 
195     /***
196      * Tests adding values.  Make sure they _DON'T_ override any other properties but add to the
197      * existing properties  and keep sequence
198      */
199     public void testAddingProperty() throws Exception
200     {
201         cc.addConfiguration(conf1);
202         cc.addConfiguration(xmlConf);
203 
204         String[] values = cc.getStringArray("test.short");
205 
206         assertEquals("Number of values before add is wrong!", 1, values.length);
207         assertEquals("First Value before add is wrong", "1", values[0]);
208 
209         cc.addProperty("test.short", "88");
210 
211         values = cc.getStringArray("test.short");
212 
213         assertEquals("Number of values is wrong!", 2, values.length);
214         assertEquals("First Value is wrong", "1", values[0]);
215         assertEquals("Third Value is wrong", "88", values[1]);
216     }
217 
218     /***
219      * Tests setting values.  These are set in memory mode only!
220      */
221     public void testSettingMissingProperty() throws Exception
222     {
223         cc.addConfiguration(conf1);
224         cc.addConfiguration(xmlConf);
225         cc.setProperty("my.new.property", "supernew");
226         assertEquals("supernew", cc.getString("my.new.property"));
227     }
228 
229     /***
230      * Tests retrieving subsets of configurations
231      */
232     public void testGettingSubset() throws Exception
233     {
234         cc.addConfiguration(conf1);
235         cc.addConfiguration(xmlConf);
236 
237         Configuration subset = cc.subset("test");
238         assertNotNull(subset);
239         assertFalse("Shouldn't be empty", subset.isEmpty());
240         assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "1", subset.getString("short"));
241 
242         cc.setProperty("test.short", "43");
243         subset = cc.subset("test");
244         assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "43", subset.getString("short"));
245     }
246 
247     /***
248      * Tests subsets and still can resolve elements
249      */
250     public void testSubsetCanResolve() throws Exception
251     {
252         cc = new CompositeConfiguration();
253         final BaseConfiguration config = new BaseConfiguration();
254         config.addProperty("subset.tempfile", "${java.io.tmpdir}/file.tmp");
255         cc.addConfiguration(config);
256         cc.addConfiguration(ConfigurationConverter.getConfiguration(System.getProperties()));
257 
258         Configuration subset = cc.subset("subset");
259         assertEquals(System.getProperty("java.io.tmpdir") + "/file.tmp", subset.getString("tempfile"));
260     }
261 
262     /***
263      * Tests <code>List</code> parsing.
264      */
265     public void testList() throws Exception
266     {
267         cc.addConfiguration(conf1);
268         cc.addConfiguration(xmlConf);
269 
270         List packages = cc.getList("packages");
271         // we should get 3 packages here
272         assertEquals(3, packages.size());
273 
274         List defaultList = new ArrayList();
275         defaultList.add("1");
276         defaultList.add("2");
277 
278         packages = cc.getList("packages.which.dont.exist", defaultList);
279         // we should get 2 packages here
280         assertEquals(2, packages.size());
281 
282     }
283 
284     /***
285      * Tests <code>String</code> array parsing.
286      */
287     public void testStringArray() throws Exception
288     {
289         cc.addConfiguration(conf1);
290         cc.addConfiguration(xmlConf);
291 
292         String[] packages = cc.getStringArray("packages");
293         // we should get 3 packages here
294         assertEquals(3, packages.length);
295 
296         packages = cc.getStringArray("packages.which.dont.exist");
297         // we should get 0 packages here
298         assertEquals(0, packages.length);
299     }
300 
301     public void testGetList()
302     {
303         Configuration conf1 = new BaseConfiguration();
304         conf1.addProperty("array", "value1");
305         conf1.addProperty("array", "value2");
306 
307         Configuration conf2 = new BaseConfiguration();
308         conf2.addProperty("array", "value3");
309         conf2.addProperty("array", "value4");
310 
311         cc.addConfiguration(conf1);
312         cc.addConfiguration(conf2);
313 
314         // check the composite 'array' property
315         List list = cc.getList("array");
316         assertNotNull("null list", list);
317         assertEquals("list size", 2, list.size());
318         assertTrue("'value1' not found in the list", list.contains("value1"));
319         assertTrue("'value2' not found in the list", list.contains("value2"));
320 
321         // add an element to the list in the composite configuration
322         cc.addProperty("array", "value5");
323 
324         // test the new list
325         list = cc.getList("array");
326         assertNotNull("null list", list);
327         assertEquals("list size", 3, list.size());
328         assertTrue("'value1' not found in the list", list.contains("value1"));
329         assertTrue("'value2' not found in the list", list.contains("value2"));
330         assertTrue("'value5' not found in the list", list.contains("value5"));
331     }
332 
333     /***
334      * Tests <code>getKeys</code> preserves the order
335      */
336     public void testGetKeysPreservesOrder() throws Exception
337     {
338         cc.addConfiguration(conf1);
339         List orderedList = new ArrayList();
340         for (Iterator keys = conf1.getKeys(); keys.hasNext();)
341         {
342             orderedList.add(keys.next());
343         }
344         List iteratedList = new ArrayList();
345         for (Iterator keys = cc.getKeys(); keys.hasNext();)
346         {
347             iteratedList.add(keys.next());
348         }
349         assertEquals(orderedList.size(), iteratedList.size());
350         for (int i = 0; i < orderedList.size(); i++)
351         {
352             assertEquals(orderedList.get(i), iteratedList.get(i));
353         }
354     }
355 
356     /***
357      * Tests <code>getKeys(String key)</code> preserves the order
358      */
359     public void testGetKeys2PreservesOrder() throws Exception
360     {
361         cc.addConfiguration(conf1);
362         List orderedList = new ArrayList();
363         for (Iterator keys = conf1.getKeys("test"); keys.hasNext();)
364         {
365             orderedList.add(keys.next());
366         }
367         List iteratedList = new ArrayList();
368         for (Iterator keys = cc.getKeys("test"); keys.hasNext();)
369         {
370             iteratedList.add(keys.next());
371         }
372         assertEquals(orderedList.size(), iteratedList.size());
373         for (int i = 0; i < orderedList.size(); i++)
374         {
375             assertEquals(orderedList.get(i), iteratedList.get(i));
376         }
377     }
378 
379     public void testGetStringWithDefaults()
380     {
381         BaseConfiguration defaults = new BaseConfiguration();
382         defaults.addProperty("default", "default string");
383 
384         CompositeConfiguration c = new CompositeConfiguration(defaults);
385         c.setThrowExceptionOnMissing(cc.isThrowExceptionOnMissing());
386         c.addProperty("string", "test string");
387 
388         assertEquals("test string", c.getString("string"));
389         try
390         {
391             c.getString("XXX");
392             fail("Should throw NoSuchElementException exception");
393         }
394         catch (NoSuchElementException e)
395         {
396             //ok
397         }
398         catch (Exception e)
399         {
400             fail("Should throw NoSuchElementException exception, not " + e);
401         }
402 
403         //test defaults
404         assertEquals("test string", c.getString("string", "some default value"));
405         assertEquals("default string", c.getString("default"));
406         assertEquals("default string", c.getString("default", "some default value"));
407         assertEquals("some default value", c.getString("XXX", "some default value"));
408     }
409 
410     public void testCheckingInMemoryConfiguration() throws Exception
411     {
412         String TEST_KEY = "testKey";
413         Configuration defaults = new PropertiesConfiguration();
414         defaults.setProperty(TEST_KEY, "testValue");
415         Configuration testConfiguration = new CompositeConfiguration(defaults);
416         assertTrue(testConfiguration.containsKey(TEST_KEY));
417         assertFalse(testConfiguration.isEmpty());
418         boolean foundTestKey = false;
419         Iterator i = testConfiguration.getKeys();
420         //assertTrue(i instanceof IteratorChain);
421         //IteratorChain ic = (IteratorChain)i;
422         //assertEquals(2,i.size());
423         for (; i.hasNext();)
424         {
425             String key = (String) i.next();
426             if (key.equals(TEST_KEY))
427             {
428                 foundTestKey = true;
429             }
430         }
431         assertTrue(foundTestKey);
432         testConfiguration.clearProperty(TEST_KEY);
433         assertFalse(testConfiguration.containsKey(TEST_KEY));
434     }
435 
436     public void testStringArrayInterpolation()
437     {
438         CompositeConfiguration config = new CompositeConfiguration();
439         config.addProperty("base", "foo");
440         config.addProperty("list", "${base}.bar1");
441         config.addProperty("list", "${base}.bar2");
442         config.addProperty("list", "${base}.bar3");
443 
444         String[] array = config.getStringArray("list");
445         assertEquals("size", 3, array.length);
446         assertEquals("1st element", "foo.bar1", array[0]);
447         assertEquals("2nd element", "foo.bar2", array[1]);
448         assertEquals("3rd element", "foo.bar3", array[2]);
449     }
450 
451     /***
452      * Tests whether global interpolation works with lists.
453      */
454     public void testListInterpolation()
455     {
456         PropertiesConfiguration c1 = new PropertiesConfiguration();
457         c1.addProperty("c1.value", "test1");
458         c1.addProperty("c1.value", "${c2.value}");
459         cc.addConfiguration(c1);
460         PropertiesConfiguration c2 = new PropertiesConfiguration();
461         c2.addProperty("c2.value", "test2");
462         cc.addConfiguration(c2);
463         List lst = cc.getList("c1.value");
464         assertEquals("Wrong list size", 2, lst.size());
465         assertEquals("Wrong first element", "test1", lst.get(0));
466         assertEquals("Wrong second element", "test2", lst.get(1));
467     }
468 
469     /***
470      * Tests interpolation in combination with reloading.
471      */
472     public void testInterpolationWithReload() throws IOException,
473             ConfigurationException
474     {
475         File testFile = new File("target/testConfig.properties");
476         final String propFirst = "first.name";
477         final String propFull = "full.name";
478 
479         try
480         {
481             writeTestConfig(testFile, propFirst, "John");
482             PropertiesConfiguration c1 = new PropertiesConfiguration(testFile);
483             c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
484             PropertiesConfiguration c2 = new PropertiesConfiguration();
485             c2.addProperty(propFull, "${" + propFirst + "} Doe");
486             CompositeConfiguration cc = new CompositeConfiguration();
487             cc.addConfiguration(c1);
488             cc.addConfiguration(c2);
489             assertEquals("Wrong name", "John Doe", cc.getString(propFull));
490 
491             writeTestConfig(testFile, propFirst, "Jane");
492             assertEquals("First name not changed", "Jane", c1
493                     .getString(propFirst));
494             assertEquals("First name not changed in composite", "Jane", cc
495                     .getString(propFirst));
496             assertEquals("Full name not changed", "Jane Doe", cc
497                     .getString(propFull));
498         }
499         finally
500         {
501             if (testFile.exists())
502             {
503                 testFile.delete();
504             }
505         }
506     }
507 
508     /***
509      * Writes a test properties file containing a single property definition.
510      *
511      * @param f the file to write
512      * @param prop the property name
513      * @param value the property value
514      * @throws IOException if an error occurs
515      */
516     private void writeTestConfig(File f, String prop, String value)
517             throws IOException
518     {
519         PrintWriter out = new PrintWriter(new FileWriter(f));
520         out.print(prop);
521         out.print("=");
522         out.println(value);
523         out.close();
524     }
525 
526     public void testInstanciateWithCollection()
527     {
528         Collection configs = new ArrayList();
529         configs.add(xmlConf);
530         configs.add(conf1);
531         configs.add(conf2);
532 
533         CompositeConfiguration config = new CompositeConfiguration(configs);
534         assertEquals("Number of configurations", 4, config.getNumberOfConfigurations());
535         assertTrue("The in memory configuration is not empty", config.getInMemoryConfiguration().isEmpty());
536     }
537 
538     public void testClone()
539     {
540         CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
541         assertEquals("Wrong number of contained configurations", cc
542                 .getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
543 
544         StrictConfigurationComparator comp = new StrictConfigurationComparator();
545         for (int i = 0; i < cc.getNumberOfConfigurations(); i++)
546         {
547             assertEquals("Wrong configuration class at " + i, cc
548                     .getConfiguration(i).getClass(), cc2.getConfiguration(i)
549                     .getClass());
550             assertNotSame("Configuration was not cloned", cc
551                     .getConfiguration(i), cc2.getConfiguration(i));
552             assertTrue("Configurations at " + i + " not equal", comp.compare(cc
553                     .getConfiguration(i), cc2.getConfiguration(i)));
554         }
555 
556         assertTrue("Configurations are not equal", comp.compare(cc, cc2));
557     }
558 
559     /***
560      * Tests cloning if one of the contained configurations does not support
561      * this operation. This should cause an exception.
562      */
563     public void testCloneNotSupported()
564     {
565         cc.addConfiguration(new NonCloneableConfiguration());
566         try
567         {
568             cc.clone();
569             fail("Could clone non cloneable configuration!");
570         }
571         catch (ConfigurationRuntimeException crex)
572         {
573             // ok
574         }
575     }
576 
577     /***
578      * Ensures that event listeners are not cloned.
579      */
580     public void testCloneEventListener()
581     {
582         cc.addConfigurationListener(new TestEventListenerImpl());
583         CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
584         assertTrue("Listeners have been cloned", cc2
585                 .getConfigurationListeners().isEmpty());
586     }
587 
588     /***
589      * Tests whether add property events are triggered.
590      */
591     public void testEventAddProperty()
592     {
593         TestEventListenerImpl l = new TestEventListenerImpl();
594         cc.addConfigurationListener(l);
595         cc.addProperty("test", "value");
596         assertEquals("No add events received", 2, l.eventCount);
597     }
598 
599     /***
600      * Tests whether set property events are triggered.
601      */
602     public void testEventSetProperty()
603     {
604         TestEventListenerImpl l = new TestEventListenerImpl();
605         cc.addConfigurationListener(l);
606         cc.setProperty("test", "value");
607         assertEquals("No set events received", 2, l.eventCount);
608     }
609 
610     /***
611      * Tests whether clear property events are triggered.
612      */
613     public void testEventClearProperty()
614     {
615         cc.addConfiguration(conf1);
616         assertTrue("Wrong value for property", cc
617                 .getBoolean("configuration.loaded"));
618         TestEventListenerImpl l = new TestEventListenerImpl();
619         cc.addConfigurationListener(l);
620         cc.clearProperty("configuration.loaded");
621         assertFalse("Key still present", cc.containsKey("configuration.loaded"));
622         assertEquals("No clear events received", 2, l.eventCount);
623     }
624 
625     /***
626      * Tests chaning the list delimiter character.
627      */
628     public void testSetListDelimiter()
629     {
630         cc.setListDelimiter('/');
631         checkSetListDelimiter();
632     }
633 
634     /***
635      * Tests whether the correct list delimiter is set after a clear operation.
636      */
637     public void testSetListDelimiterAfterClear()
638     {
639         cc.setListDelimiter('/');
640         cc.clear();
641         checkSetListDelimiter();
642     }
643 
644     /***
645      * Helper method for testing whether the list delimiter is correctly
646      * handled.
647      */
648     private void checkSetListDelimiter()
649     {
650         cc.addProperty("test.list", "a/b/c");
651         cc.addProperty("test.property", "a,b,c");
652         assertEquals("Wrong number of list elements", 3, cc
653                 .getList("test.list").size());
654         assertEquals("Wrong value of property", "a,b,c", cc
655                 .getString("test.property"));
656     }
657 
658     /***
659      * Tests whether list splitting can be disabled.
660      */
661     public void testSetDelimiterParsingDisabled()
662     {
663         cc.setDelimiterParsingDisabled(true);
664         checkSetListDelimiterParsingDisabled();
665     }
666 
667     /***
668      * Tests whether the list parsing flag is correctly handled after a clear()
669      * operation.
670      */
671     public void testSetDelimiterParsingDisabledAfterClear()
672     {
673         cc.setDelimiterParsingDisabled(true);
674         cc.clear();
675         checkSetListDelimiterParsingDisabled();
676     }
677 
678     /***
679      * Helper method for checking whether the list parsing flag is correctly
680      * handled.
681      */
682     private void checkSetListDelimiterParsingDisabled()
683     {
684         cc.addProperty("test.property", "a,b,c");
685         assertEquals("Wrong value of property", "a,b,c", cc
686                 .getString("test.property"));
687     }
688 
689     /***
690      * Prepares a test of the getSource() method.
691      */
692     private void setUpSourceTest()
693     {
694         cc.addConfiguration(conf1);
695         cc.addConfiguration(conf2);
696     }
697 
698     /***
699      * Tests the getSource() method if the property is defined in a single child
700      * configuration.
701      */
702     public void testGetSourceSingle()
703     {
704         setUpSourceTest();
705         conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
706         assertSame("Wrong source configuration", conf1, cc
707                 .getSource(TEST_PROPERTY));
708     }
709 
710     /***
711      * Tests the getSource() method for an unknown property key.
712      */
713     public void testGetSourceUnknown()
714     {
715         setUpSourceTest();
716         assertNull("Wrong source for unknown key", cc.getSource(TEST_PROPERTY));
717     }
718 
719     /***
720      * Tests the getSource() method for a property contained in the in memory
721      * configuration.
722      */
723     public void testGetSourceInMemory()
724     {
725         setUpSourceTest();
726         cc.addProperty(TEST_PROPERTY, Boolean.TRUE);
727         assertSame("Source not found in in-memory config", cc
728                 .getInMemoryConfiguration(), cc.getSource(TEST_PROPERTY));
729     }
730 
731     /***
732      * Tests the getSource() method if the property is defined by multiple child
733      * configurations. In this case an exception should be thrown.
734      */
735     public void testGetSourceMultiple()
736     {
737         setUpSourceTest();
738         conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
739         cc.addProperty(TEST_PROPERTY, "a value");
740         try
741         {
742             cc.getSource(TEST_PROPERTY);
743             fail("Property in multiple configurations did not cause an error!");
744         }
745         catch (IllegalArgumentException iex)
746         {
747             // ok
748         }
749     }
750 
751     /***
752      * Tests the getSource() method for a null key. This should cause an
753      * exception.
754      */
755     public void testGetSourceNull()
756     {
757         try
758         {
759             cc.getSource(null);
760             fail("Could pass null key to getSource()!");
761         }
762         catch (IllegalArgumentException iex)
763         {
764             // ok
765         }
766     }
767 
768     /***
769      * Prepares a test for interpolation with multiple configurations and
770      * similar properties.
771      */
772     private void prepareInterpolationTest()
773     {
774         PropertiesConfiguration p = new PropertiesConfiguration();
775         p.addProperty("foo", "initial");
776         p.addProperty("bar", "${foo}");
777         p.addProperty("prefix.foo", "override");
778 
779         cc.addConfiguration(p.subset("prefix"));
780         cc.addConfiguration(p);
781         assertEquals("Wrong value on direct access", "override", cc
782                 .getString("bar"));
783     }
784 
785     /***
786      * Tests querying a list when a tricky interpolation is involved. This is
787      * related to CONFIGURATION-339.
788      */
789     public void testGetListWithInterpolation()
790     {
791         prepareInterpolationTest();
792         List lst = cc.getList("bar");
793         assertEquals("Wrong number of values", 1, lst.size());
794         assertEquals("Wrong value in list", "override", lst.get(0));
795     }
796 
797     /***
798      * Tests querying a string array when a tricky interpolation is involved.
799      */
800     public void testGetStringArrayWithInterpolation()
801     {
802         prepareInterpolationTest();
803         String[] values = cc.getStringArray("bar");
804         assertEquals("Wrong number of values", 1, values.length);
805         assertEquals("Wrong value in array", "override", values[0]);
806     }
807 
808     /***
809      * A test configuration event listener that counts the number of received
810      * events. Used for testing the event facilities.
811      */
812     static class TestEventListenerImpl implements ConfigurationListener
813     {
814         /*** The number of received events.*/
815         int eventCount;
816 
817         public void configurationChanged(ConfigurationEvent event)
818         {
819             eventCount++;
820         }
821     }
822 }