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.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  import java.util.Collection;
26  
27  import org.apache.commons.configuration.event.ConfigurationEvent;
28  import org.apache.commons.configuration.event.ConfigurationListener;
29  
30  import junit.framework.TestCase;
31  
32  /***
33   * Test loading multiple configurations.
34   *
35   * @version $Id: TestCompositeConfiguration.java 439648 2006-09-02 20:42:10Z oheger $
36   */
37  public class TestCompositeConfiguration extends TestCase
38  {
39      protected PropertiesConfiguration conf1;
40      protected PropertiesConfiguration conf2;
41      protected XMLConfiguration xmlConf;
42      protected CompositeConfiguration cc;
43  
44      /***
45       * The File that we test with
46       */
47      private String testProperties = new File("conf/test.properties").getAbsolutePath();
48      private String testProperties2 = new File("conf/test2.properties").getAbsolutePath();
49      private String testPropertiesXML = new File("conf/test.xml").getAbsolutePath();
50  
51      protected void setUp() throws Exception
52      {
53          cc = new CompositeConfiguration();
54          conf1 = new PropertiesConfiguration(testProperties);
55          conf2 = new PropertiesConfiguration(testProperties2);
56          xmlConf = new XMLConfiguration(new File(testPropertiesXML));
57  
58          cc.setThrowExceptionOnMissing(true);
59      }
60  
61      public void testThrowExceptionOnMissing()
62      {
63          assertTrue("Throw Exception Property is not set!", cc.isThrowExceptionOnMissing());
64      }
65  
66      public void testAddRemoveConfigurations() throws Exception
67      {
68          cc.addConfiguration(conf1);
69          assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
70          cc.addConfiguration(conf1);
71          assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
72          cc.addConfiguration(conf2);
73          assertEquals("Number of configurations", 3, cc.getNumberOfConfigurations());
74          cc.removeConfiguration(conf1);
75          assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
76          cc.clear();
77          assertEquals("Number of configurations", 1, cc.getNumberOfConfigurations());
78      }
79  
80      public void testGetPropertyWIncludes() throws Exception
81      {
82          cc.addConfiguration(conf1);
83          cc.addConfiguration(conf2);
84          List l = cc.getList("packages");
85          assertTrue(l.contains("packagea"));
86      }
87  
88      public void testGetProperty() throws Exception
89      {
90          cc.addConfiguration(conf1);
91          cc.addConfiguration(conf2);
92          assertEquals("Make sure we get the property from conf1 first", "test.properties", cc.getString("propertyInOrder"));
93          cc.clear();
94  
95          cc.addConfiguration(conf2);
96          cc.addConfiguration(conf1);
97          assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
98      }
99  
100     public void testCantRemoveMemoryConfig() throws Exception
101     {
102         cc.clear();
103         assertEquals(1, cc.getNumberOfConfigurations());
104 
105         Configuration internal = cc.getConfiguration(0);
106         cc.removeConfiguration(internal);
107 
108         assertEquals(1, cc.getNumberOfConfigurations());
109     }
110 
111     public void testGetPropertyMissing() throws Exception
112     {
113         cc.addConfiguration(conf1);
114         cc.addConfiguration(conf2);
115         try
116         {
117             assertNull(cc.getString("bogus.property"));
118             fail("Should have thrown a NoSuchElementException");
119         }
120         catch (NoSuchElementException nsee)
121         {
122             assertTrue(nsee.getMessage().indexOf("bogus.property") > -1);
123         }
124 
125         assertTrue("Should be false", !cc.getBoolean("test.missing.boolean", false));
126         assertTrue("Should be true", cc.getBoolean("test.missing.boolean.true", true));
127     }
128 
129     /***
130      * Tests <code>List</code> parsing.
131      */
132     public void testMultipleTypesOfConfigs() throws Exception
133     {
134         cc.addConfiguration(conf1);
135         cc.addConfiguration(xmlConf);
136         assertEquals("Make sure we get the property from conf1 first", 1, cc.getInt("test.short"));
137         cc.clear();
138 
139         cc.addConfiguration(xmlConf);
140         cc.addConfiguration(conf1);
141         assertEquals("Make sure we get the property from xml", 8, cc.getInt("test.short"));
142     }
143 
144     /***
145      * Tests <code>List</code> parsing.
146      */
147     public void testPropertyExistsInOnlyOneConfig() throws Exception
148     {
149         cc.addConfiguration(conf1);
150         cc.addConfiguration(xmlConf);
151         assertEquals("value", cc.getString("element"));
152     }
153 
154     /***
155      * Tests getting a default when the key doesn't exist
156      */
157     public void testDefaultValueWhenKeyMissing() throws Exception
158     {
159         cc.addConfiguration(conf1);
160         cc.addConfiguration(xmlConf);
161         assertEquals("default", cc.getString("bogus", "default"));
162         assertTrue(1.4 == cc.getDouble("bogus", 1.4));
163         assertTrue(1.4 == cc.getDouble("bogus", 1.4));
164     }
165 
166     /***
167      * Tests <code>List</code> parsing.
168      */
169     public void testGettingConfiguration() throws Exception
170     {
171         cc.addConfiguration(conf1);
172         cc.addConfiguration(xmlConf);
173         assertEquals(PropertiesConfiguration.class, cc.getConfiguration(0).getClass());
174         assertEquals(XMLConfiguration.class, cc.getConfiguration(1).getClass());
175     }
176 
177     /***
178      * Tests setting values.  These are set in memory mode only!
179      */
180     public void testClearingProperty() throws Exception
181     {
182         cc.addConfiguration(conf1);
183         cc.addConfiguration(xmlConf);
184         cc.clearProperty("test.short");
185         assertTrue("Make sure test.short is gone!", !cc.containsKey("test.short"));
186     }
187 
188     /***
189      * Tests adding values.  Make sure they _DON'T_ override any other properties but add to the
190      * existing properties  and keep sequence
191      */
192     public void testAddingProperty() throws Exception
193     {
194         cc.addConfiguration(conf1);
195         cc.addConfiguration(xmlConf);
196 
197         String[] values = cc.getStringArray("test.short");
198 
199         assertEquals("Number of values before add is wrong!", 1, values.length);
200         assertEquals("First Value before add is wrong", "1", values[0]);
201 
202         cc.addProperty("test.short", "88");
203 
204         values = cc.getStringArray("test.short");
205 
206         assertEquals("Number of values is wrong!", 2, values.length);
207         assertEquals("First Value is wrong", "1", values[0]);
208         assertEquals("Third Value is wrong", "88", values[1]);
209     }
210 
211     /***
212      * Tests setting values.  These are set in memory mode only!
213      */
214     public void testSettingMissingProperty() throws Exception
215     {
216         cc.addConfiguration(conf1);
217         cc.addConfiguration(xmlConf);
218         cc.setProperty("my.new.property", "supernew");
219         assertEquals("supernew", cc.getString("my.new.property"));
220     }
221 
222     /***
223      * Tests retrieving subsets of configurations
224      */
225     public void testGettingSubset() throws Exception
226     {
227         cc.addConfiguration(conf1);
228         cc.addConfiguration(xmlConf);
229 
230         Configuration subset = cc.subset("test");
231         assertNotNull(subset);
232         assertFalse("Shouldn't be empty", subset.isEmpty());
233         assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "1", subset.getString("short"));
234 
235         cc.setProperty("test.short", "43");
236         subset = cc.subset("test");
237         assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "43", subset.getString("short"));
238     }
239 
240     /***
241      * Tests subsets and still can resolve elements
242      */
243     public void testSubsetCanResolve() throws Exception
244     {
245         cc = new CompositeConfiguration();
246         final BaseConfiguration config = new BaseConfiguration();
247         config.addProperty("subset.tempfile", "${java.io.tmpdir}/file.tmp");
248         cc.addConfiguration(config);
249         cc.addConfiguration(ConfigurationConverter.getConfiguration(System.getProperties()));
250 
251         Configuration subset = cc.subset("subset");
252         assertEquals(System.getProperty("java.io.tmpdir") + "/file.tmp", subset.getString("tempfile"));
253     }
254 
255     /***
256      * Tests <code>List</code> parsing.
257      */
258     public void testList() throws Exception
259     {
260         cc.addConfiguration(conf1);
261         cc.addConfiguration(xmlConf);
262 
263         List packages = cc.getList("packages");
264         // we should get 3 packages here
265         assertEquals(3, packages.size());
266 
267         List defaultList = new ArrayList();
268         defaultList.add("1");
269         defaultList.add("2");
270 
271         packages = cc.getList("packages.which.dont.exist", defaultList);
272         // we should get 2 packages here
273         assertEquals(2, packages.size());
274 
275     }
276 
277     /***
278      * Tests <code>String</code> array parsing.
279      */
280     public void testStringArray() throws Exception
281     {
282         cc.addConfiguration(conf1);
283         cc.addConfiguration(xmlConf);
284 
285         String[] packages = cc.getStringArray("packages");
286         // we should get 3 packages here
287         assertEquals(3, packages.length);
288 
289         packages = cc.getStringArray("packages.which.dont.exist");
290         // we should get 0 packages here
291         assertEquals(0, packages.length);
292     }
293 
294     public void testGetList()
295     {
296         Configuration conf1 = new BaseConfiguration();
297         conf1.addProperty("array", "value1");
298         conf1.addProperty("array", "value2");
299 
300         Configuration conf2 = new BaseConfiguration();
301         conf2.addProperty("array", "value3");
302         conf2.addProperty("array", "value4");
303 
304         cc.addConfiguration(conf1);
305         cc.addConfiguration(conf2);
306 
307         // check the composite 'array' property
308         List list = cc.getList("array");
309         assertNotNull("null list", list);
310         assertEquals("list size", 2, list.size());
311         assertTrue("'value1' not found in the list", list.contains("value1"));
312         assertTrue("'value2' not found in the list", list.contains("value2"));
313 
314         // add an element to the list in the composite configuration
315         cc.addProperty("array", "value5");
316 
317         // test the new list
318         list = cc.getList("array");
319         assertNotNull("null list", list);
320         assertEquals("list size", 3, list.size());
321         assertTrue("'value1' not found in the list", list.contains("value1"));
322         assertTrue("'value2' not found in the list", list.contains("value2"));
323         assertTrue("'value5' not found in the list", list.contains("value5"));
324     }
325 
326     /***
327      * Tests <code>getKeys</code> preserves the order
328      */
329     public void testGetKeysPreservesOrder() throws Exception
330     {
331         cc.addConfiguration(conf1);
332         List orderedList = new ArrayList();
333         for (Iterator keys = conf1.getKeys(); keys.hasNext();)
334         {
335             orderedList.add(keys.next());
336         }
337         List iteratedList = new ArrayList();
338         for (Iterator keys = cc.getKeys(); keys.hasNext();)
339         {
340             iteratedList.add(keys.next());
341         }
342         assertEquals(orderedList.size(), iteratedList.size());
343         for (int i = 0; i < orderedList.size(); i++)
344         {
345             assertEquals(orderedList.get(i), iteratedList.get(i));
346         }
347     }
348 
349     /***
350      * Tests <code>getKeys(String key)</code> preserves the order
351      */
352     public void testGetKeys2PreservesOrder() throws Exception
353     {
354         cc.addConfiguration(conf1);
355         List orderedList = new ArrayList();
356         for (Iterator keys = conf1.getKeys("test"); keys.hasNext();)
357         {
358             orderedList.add(keys.next());
359         }
360         List iteratedList = new ArrayList();
361         for (Iterator keys = cc.getKeys("test"); keys.hasNext();)
362         {
363             iteratedList.add(keys.next());
364         }
365         assertEquals(orderedList.size(), iteratedList.size());
366         for (int i = 0; i < orderedList.size(); i++)
367         {
368             assertEquals(orderedList.get(i), iteratedList.get(i));
369         }
370     }
371 
372     public void testGetStringWithDefaults()
373     {
374         BaseConfiguration defaults = new BaseConfiguration();
375         defaults.addProperty("default", "default string");
376 
377         CompositeConfiguration c = new CompositeConfiguration(defaults);
378         c.setThrowExceptionOnMissing(cc.isThrowExceptionOnMissing());
379         c.addProperty("string", "test string");
380 
381         assertEquals("test string", c.getString("string"));
382         try
383         {
384             c.getString("XXX");
385             fail("Should throw NoSuchElementException exception");
386         }
387         catch (NoSuchElementException e)
388         {
389             //ok
390         }
391         catch (Exception e)
392         {
393             fail("Should throw NoSuchElementException exception, not " + e);
394         }
395 
396         //test defaults
397         assertEquals("test string", c.getString("string", "some default value"));
398         assertEquals("default string", c.getString("default"));
399         assertEquals("default string", c.getString("default", "some default value"));
400         assertEquals("some default value", c.getString("XXX", "some default value"));
401     }
402 
403     public void testCheckingInMemoryConfiguration() throws Exception
404     {
405         String TEST_KEY = "testKey";
406         Configuration defaults = new PropertiesConfiguration();
407         defaults.setProperty(TEST_KEY, "testValue");
408         Configuration testConfiguration = new CompositeConfiguration(defaults);
409         assertTrue(testConfiguration.containsKey(TEST_KEY));
410         assertFalse(testConfiguration.isEmpty());
411         boolean foundTestKey = false;
412         Iterator i = testConfiguration.getKeys();
413         //assertTrue(i instanceof IteratorChain);
414         //IteratorChain ic = (IteratorChain)i;
415         //assertEquals(2,i.size());
416         for (; i.hasNext();)
417         {
418             String key = (String) i.next();
419             if (key.equals(TEST_KEY))
420             {
421                 foundTestKey = true;
422             }
423         }
424         assertTrue(foundTestKey);
425         testConfiguration.clearProperty(TEST_KEY);
426         assertFalse(testConfiguration.containsKey(TEST_KEY));
427     }
428 
429     public void testStringArrayInterpolation()
430     {
431         CompositeConfiguration config = new CompositeConfiguration();
432         config.addProperty("base", "foo");
433         config.addProperty("list", "${base}.bar1");
434         config.addProperty("list", "${base}.bar2");
435         config.addProperty("list", "${base}.bar3");
436 
437         String[] array = config.getStringArray("list");
438         assertEquals("size", 3, array.length);
439         assertEquals("1st element", "foo.bar1", array[0]);
440         assertEquals("2nd element", "foo.bar2", array[1]);
441         assertEquals("3rd element", "foo.bar3", array[2]);
442     }
443 
444     public void testInstanciateWithCollection()
445     {
446         Collection configs = new ArrayList();
447         configs.add(xmlConf);
448         configs.add(conf1);
449         configs.add(conf2);
450 
451         CompositeConfiguration config = new CompositeConfiguration(configs);
452         assertEquals("Number of configurations", 4, config.getNumberOfConfigurations());
453         assertTrue("The in memory configuration is not empty", config.getInMemoryConfiguration().isEmpty());
454     }
455 
456     public void testClone()
457     {
458         CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
459         assertEquals("Wrong number of contained configurations", cc
460                 .getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
461 
462         StrictConfigurationComparator comp = new StrictConfigurationComparator();
463         for (int i = 0; i < cc.getNumberOfConfigurations(); i++)
464         {
465             assertEquals("Wrong configuration class at " + i, cc
466                     .getConfiguration(i).getClass(), cc2.getConfiguration(i)
467                     .getClass());
468             assertNotSame("Configuration was not cloned", cc
469                     .getConfiguration(i), cc2.getConfiguration(i));
470             assertTrue("Configurations at " + i + " not equal", comp.compare(cc
471                     .getConfiguration(i), cc2.getConfiguration(i)));
472         }
473 
474         assertTrue("Configurations are not equal", comp.compare(cc, cc2));
475     }
476 
477     /***
478      * Tests cloning if one of the contained configurations does not support
479      * this operation. This should cause an exception.
480      */
481     public void testCloneNotSupported()
482     {
483         cc.addConfiguration(new NonCloneableConfiguration());
484         try
485         {
486             cc.clone();
487             fail("Could clone non cloneable configuration!");
488         }
489         catch (ConfigurationRuntimeException crex)
490         {
491             // ok
492         }
493     }
494 
495     /***
496      * Ensures that event listeners are not cloned.
497      */
498     public void testCloneEventListener()
499     {
500         cc.addConfigurationListener(new ConfigurationListener()
501         {
502             public void configurationChanged(ConfigurationEvent event)
503             {
504                 // Just a dummy
505             }
506         });
507         CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
508         assertTrue("Listeners have been cloned", cc2
509                 .getConfigurationListeners().isEmpty());
510     }
511 }