1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
18
19 import java.io.File;
20 import java.io.FileWriter;
21 import java.io.IOException;
22 import java.io.PrintWriter;
23 import java.util.Collection;
24 import java.util.Set;
25
26 import org.apache.commons.configuration.event.ConfigurationEvent;
27 import org.apache.commons.configuration.event.ConfigurationListener;
28 import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
29 import org.apache.commons.configuration.tree.NodeCombiner;
30 import org.apache.commons.configuration.tree.UnionCombiner;
31
32 import junit.framework.Assert;
33 import junit.framework.TestCase;
34
35 /***
36 * Test class for CombinedConfiguration.
37 *
38 * @version $Id: TestCombinedConfiguration.java 555737 2007-07-12 19:59:25Z oheger $
39 */
40 public class TestCombinedConfiguration extends TestCase
41 {
42 /*** Constant for the name of a sub configuration. */
43 static final String TEST_NAME = "SUBCONFIG";
44
45 /*** Constant for a test key. */
46 static final String TEST_KEY = "test.value";
47
48 /*** Constant for the name of the first child configuration.*/
49 static final String CHILD1 = TEST_NAME + "1";
50
51 /*** Constant for the name of the second child configuration.*/
52 static final String CHILD2 = TEST_NAME + "2";
53
54 /*** The configuration to be tested. */
55 CombinedConfiguration config;
56
57 /*** The test event listener. */
58 CombinedListener listener;
59
60 protected void setUp() throws Exception
61 {
62 super.setUp();
63 config = new CombinedConfiguration();
64 listener = new CombinedListener();
65 config.addConfigurationListener(listener);
66 }
67
68 /***
69 * Tests accessing a newly created combined configuration.
70 */
71 public void testInit()
72 {
73 assertEquals("Already configurations contained", 0, config
74 .getNumberOfConfigurations());
75 assertTrue("Set of names is not empty", config.getConfigurationNames()
76 .isEmpty());
77 assertTrue("Wrong node combiner",
78 config.getNodeCombiner() instanceof UnionCombiner);
79 assertNull("Test config was found", config.getConfiguration(TEST_NAME));
80 assertFalse("Force reload check flag is set", config.isForceReloadCheck());
81 }
82
83 /***
84 * Tests adding a configuration (without further information).
85 */
86 public void testAddConfiguration()
87 {
88 AbstractConfiguration c = setUpTestConfiguration();
89 config.addConfiguration(c);
90 checkAddConfig(c);
91 assertEquals("Wrong number of configs", 1, config
92 .getNumberOfConfigurations());
93 assertTrue("Name list is not empty", config.getConfigurationNames()
94 .isEmpty());
95 assertSame("Added config not found", c, config.getConfiguration(0));
96 assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
97 listener.checkEvent(1, 0);
98 }
99
100 /***
101 * Tests adding a configuration with a name.
102 */
103 public void testAddConfigurationWithName()
104 {
105 AbstractConfiguration c = setUpTestConfiguration();
106 config.addConfiguration(c, TEST_NAME);
107 checkAddConfig(c);
108 assertEquals("Wrong number of configs", 1, config
109 .getNumberOfConfigurations());
110 assertSame("Added config not found", c, config.getConfiguration(0));
111 assertSame("Added config not found by name", c, config
112 .getConfiguration(TEST_NAME));
113 Set names = config.getConfigurationNames();
114 assertEquals("Wrong number of config names", 1, names.size());
115 assertTrue("Name not found", names.contains(TEST_NAME));
116 assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
117 listener.checkEvent(1, 0);
118 }
119
120 /***
121 * Tests adding a configuration with a name when this name already exists.
122 * This should cause an exception.
123 */
124 public void testAddConfigurationWithNameTwice()
125 {
126 config.addConfiguration(setUpTestConfiguration(), TEST_NAME);
127 try
128 {
129 config.addConfiguration(setUpTestConfiguration(), TEST_NAME,
130 "prefix");
131 fail("Could add config with same name!");
132 }
133 catch (ConfigurationRuntimeException cex)
134 {
135
136 }
137 }
138
139 /***
140 * Tests adding a configuration and specifying an at position.
141 */
142 public void testAddConfigurationAt()
143 {
144 AbstractConfiguration c = setUpTestConfiguration();
145 config.addConfiguration(c, null, "my");
146 checkAddConfig(c);
147 assertTrue("Wrong property value", config.getBoolean("my." + TEST_KEY));
148 }
149
150 /***
151 * Tests adding a configuration with a complex at position. Here the at path
152 * contains a dot, which must be escaped.
153 */
154 public void testAddConfigurationComplexAt()
155 {
156 AbstractConfiguration c = setUpTestConfiguration();
157 config.addConfiguration(c, null, "This..is.a.complex");
158 checkAddConfig(c);
159 assertTrue("Wrong property value", config
160 .getBoolean("This..is.a.complex." + TEST_KEY));
161 }
162
163 /***
164 * Checks if a configuration was correctly added to the combined config.
165 *
166 * @param c the config to check
167 */
168 private void checkAddConfig(AbstractConfiguration c)
169 {
170 Collection listeners = c.getConfigurationListeners();
171 assertEquals("Wrong number of configuration listeners", 1, listeners
172 .size());
173 assertTrue("Combined config is no listener", listeners.contains(config));
174 }
175
176 /***
177 * Tests adding a null configuration. This should cause an exception to be
178 * thrown.
179 */
180 public void testAddNullConfiguration()
181 {
182 try
183 {
184 config.addConfiguration(null);
185 fail("Could add null configuration!");
186 }
187 catch (IllegalArgumentException iex)
188 {
189
190 }
191 }
192
193 /***
194 * Tests accessing properties if no configurations have been added.
195 */
196 public void testAccessPropertyEmpty()
197 {
198 assertFalse("Found a key", config.containsKey(TEST_KEY));
199 assertNull("Key has a value", config.getString("test.comment"));
200 assertTrue("Config is not empty", config.isEmpty());
201 }
202
203 /***
204 * Tests accessing properties if multiple configurations have been added.
205 */
206 public void testAccessPropertyMulti()
207 {
208 config.addConfiguration(setUpTestConfiguration());
209 config.addConfiguration(setUpTestConfiguration(), null, "prefix1");
210 config.addConfiguration(setUpTestConfiguration(), null, "prefix2");
211 assertTrue("Prop1 not found", config.getBoolean(TEST_KEY));
212 assertTrue("Prop 2 not found", config.getBoolean("prefix1." + TEST_KEY));
213 assertTrue("Prop 3 not found", config.getBoolean("prefix2." + TEST_KEY));
214 assertFalse("Configuration is empty", config.isEmpty());
215 listener.checkEvent(3, 0);
216 }
217
218 /***
219 * Tests removing a configuration.
220 */
221 public void testRemoveConfiguration()
222 {
223 AbstractConfiguration c = setUpTestConfiguration();
224 config.addConfiguration(c);
225 checkAddConfig(c);
226 assertTrue("Config could not be removed", config.removeConfiguration(c));
227 checkRemoveConfig(c);
228 }
229
230 /***
231 * Tests removing a configuration by index.
232 */
233 public void testRemoveConfigurationAt()
234 {
235 AbstractConfiguration c = setUpTestConfiguration();
236 config.addConfiguration(c);
237 assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
238 checkRemoveConfig(c);
239 }
240
241 /***
242 * Tests removing a configuration by name.
243 */
244 public void testRemoveConfigurationByName()
245 {
246 AbstractConfiguration c = setUpTestConfiguration();
247 config.addConfiguration(c, TEST_NAME);
248 assertSame("Wrong config removed", c, config
249 .removeConfiguration(TEST_NAME));
250 checkRemoveConfig(c);
251 }
252
253 /***
254 * Tests removing a configuration with a name.
255 */
256 public void testRemoveNamedConfiguration()
257 {
258 AbstractConfiguration c = setUpTestConfiguration();
259 config.addConfiguration(c, TEST_NAME);
260 config.removeConfiguration(c);
261 checkRemoveConfig(c);
262 }
263
264 /***
265 * Tests removing a named configuration by index.
266 */
267 public void testRemoveNamedConfigurationAt()
268 {
269 AbstractConfiguration c = setUpTestConfiguration();
270 config.addConfiguration(c, TEST_NAME);
271 assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
272 checkRemoveConfig(c);
273 }
274
275 /***
276 * Tests removing a configuration that was not added prior.
277 */
278 public void testRemoveNonContainedConfiguration()
279 {
280 assertFalse("Could remove non contained config", config
281 .removeConfiguration(setUpTestConfiguration()));
282 listener.checkEvent(0, 0);
283 }
284
285 /***
286 * Tests removing a configuration by name, which is not contained.
287 */
288 public void testRemoveConfigurationByUnknownName()
289 {
290 assertNull("Could remove configuration by unknown name", config
291 .removeConfiguration("unknownName"));
292 listener.checkEvent(0, 0);
293 }
294
295 /***
296 * Tests whether a configuration was completely removed.
297 *
298 * @param c the removed configuration
299 */
300 private void checkRemoveConfig(AbstractConfiguration c)
301 {
302 assertTrue("Listener was not removed", c.getConfigurationListeners()
303 .isEmpty());
304 assertEquals("Wrong number of contained configs", 0, config
305 .getNumberOfConfigurations());
306 assertTrue("Name was not removed", config.getConfigurationNames()
307 .isEmpty());
308 listener.checkEvent(2, 0);
309 }
310
311 /***
312 * Tests if an update of a contained configuration leeds to an invalidation
313 * of the combined configuration.
314 */
315 public void testUpdateContainedConfiguration()
316 {
317 AbstractConfiguration c = setUpTestConfiguration();
318 config.addConfiguration(c);
319 c.addProperty("test.otherTest", "yes");
320 assertEquals("New property not found", "yes", config
321 .getString("test.otherTest"));
322 listener.checkEvent(3, 0);
323 }
324
325 /***
326 * Tests if setting a node combiner causes an invalidation.
327 */
328 public void testSetNodeCombiner()
329 {
330 NodeCombiner combiner = new UnionCombiner();
331 config.setNodeCombiner(combiner);
332 assertSame("Node combiner was not set", combiner, config
333 .getNodeCombiner());
334 listener.checkEvent(1, 0);
335 }
336
337 /***
338 * Tests setting a null node combiner. This should cause an exception.
339 */
340 public void testSetNullNodeCombiner()
341 {
342 try
343 {
344 config.setNodeCombiner(null);
345 fail("Could set null node combiner!");
346 }
347 catch (IllegalArgumentException iex)
348 {
349
350 }
351 }
352
353 /***
354 * Tests cloning a combined configuration.
355 */
356 public void testClone()
357 {
358 config.addConfiguration(setUpTestConfiguration());
359 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
360 config.addConfiguration(new PropertiesConfiguration(), "props");
361
362 CombinedConfiguration cc2 = (CombinedConfiguration) config.clone();
363 assertEquals("Wrong number of contained configurations", config
364 .getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
365 assertSame("Wrong node combiner", config.getNodeCombiner(), cc2
366 .getNodeCombiner());
367 assertEquals("Wrong number of names", config.getConfigurationNames()
368 .size(), cc2.getConfigurationNames().size());
369 assertTrue("Event listeners were cloned", cc2
370 .getConfigurationListeners().isEmpty());
371
372 StrictConfigurationComparator comp = new StrictConfigurationComparator();
373 for (int i = 0; i < config.getNumberOfConfigurations(); i++)
374 {
375 assertNotSame("Configuration at " + i + " was not cloned", config
376 .getConfiguration(i), cc2.getConfiguration(i));
377 assertEquals("Wrong config class at " + i, config.getConfiguration(
378 i).getClass(), cc2.getConfiguration(i).getClass());
379 assertTrue("Configs not equal at " + i, comp.compare(config
380 .getConfiguration(i), cc2.getConfiguration(i)));
381 }
382
383 assertTrue("Combined configs not equal", comp.compare(config, cc2));
384 }
385
386 /***
387 * Tests if the cloned configuration is decoupled from the original.
388 */
389 public void testCloneModify()
390 {
391 config.addConfiguration(setUpTestConfiguration(), TEST_NAME);
392 CombinedConfiguration cc2 = (CombinedConfiguration) config.clone();
393 assertTrue("Name is missing", cc2.getConfigurationNames().contains(
394 TEST_NAME));
395 cc2.removeConfiguration(TEST_NAME);
396 assertFalse("Names in original changed", config.getConfigurationNames()
397 .isEmpty());
398 }
399
400 /***
401 * Tests clearing a combined configuration. This should remove all contained
402 * configurations.
403 */
404 public void testClear()
405 {
406 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "test");
407 config.addConfiguration(setUpTestConfiguration());
408
409 config.clear();
410 assertEquals("Still configs contained", 0, config
411 .getNumberOfConfigurations());
412 assertTrue("Still names contained", config.getConfigurationNames()
413 .isEmpty());
414 assertTrue("Config is not empty", config.isEmpty());
415
416 listener.checkEvent(3, 2);
417 }
418
419 /***
420 * Tests if file-based configurations can be reloaded.
421 */
422 public void testReloading() throws Exception
423 {
424 config.setForceReloadCheck(true);
425 File testDir = new File("target");
426 File testXmlFile = new File(testDir, "reload.xml");
427 File testPropsFile = new File(testDir, "reload.properties");
428 writeFile(testXmlFile, "<xml><xmlReload>0</xmlReload></xml>");
429 writeFile(testPropsFile, "propsReload = 0");
430 XMLConfiguration c1 = new XMLConfiguration(testXmlFile);
431 c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
432 PropertiesConfiguration c2 = new PropertiesConfiguration(testPropsFile);
433 c2.setThrowExceptionOnMissing(true);
434 c2.setReloadingStrategy(new FileAlwaysReloadingStrategy());
435 config.addConfiguration(c1);
436 config.addConfiguration(c2);
437 assertEquals("Wrong xml reload value", 0, config.getInt("xmlReload"));
438 assertEquals("Wrong props reload value", 0, config
439 .getInt("propsReload"));
440
441 writeFile(testXmlFile, "<xml><xmlReload>1</xmlReload></xml>");
442 assertEquals("XML reload not detected", 1, config.getInt("xmlReload"));
443 config.setForceReloadCheck(false);
444 writeFile(testPropsFile, "propsReload = 1");
445 assertEquals("Props reload detected though check flag is false", 0, config
446 .getInt("propsReload"));
447
448 assertTrue("XML file cannot be removed", testXmlFile.delete());
449 assertTrue("Props file cannot be removed", testPropsFile.delete());
450 }
451
452 /***
453 * Prepares a test of the getSource() method.
454 */
455 private void setUpSourceTest()
456 {
457 HierarchicalConfiguration c1 = new HierarchicalConfiguration();
458 PropertiesConfiguration c2 = new PropertiesConfiguration();
459 c1.addProperty(TEST_KEY, TEST_NAME);
460 c2.addProperty("another.key", "test");
461 config.addConfiguration(c1, CHILD1);
462 config.addConfiguration(c2, CHILD2);
463 }
464
465 /***
466 * Tests the gestSource() method when the source property is defined in a
467 * hierarchical configuration.
468 */
469 public void testGetSourceHierarchical()
470 {
471 setUpSourceTest();
472 assertEquals("Wrong source configuration", config
473 .getConfiguration(CHILD1), config.getSource(TEST_KEY));
474 }
475
476 /***
477 * Tests whether the source configuration can be detected for non
478 * hierarchical configurations.
479 */
480 public void testGetSourceNonHierarchical()
481 {
482 setUpSourceTest();
483 assertEquals("Wrong source configuration", config
484 .getConfiguration(CHILD2), config.getSource("another.key"));
485 }
486
487 /***
488 * Tests the getSource() method when the passed in key is not contained.
489 * Result should be null in this case.
490 */
491 public void testGetSourceUnknown()
492 {
493 setUpSourceTest();
494 assertNull("Wrong result for unknown key", config
495 .getSource("an.unknown.key"));
496 }
497
498 /***
499 * Tests the getSource() method when a null key is passed in. This should
500 * cause an exception.
501 */
502 public void testGetSourceNull()
503 {
504 try
505 {
506 config.getSource(null);
507 fail("Could resolve source for null key!");
508 }
509 catch (IllegalArgumentException iex)
510 {
511
512 }
513 }
514
515 /***
516 * Tests the getSource() method when the passed in key belongs to the
517 * combined configuration itself.
518 */
519 public void testGetSourceCombined()
520 {
521 setUpSourceTest();
522 final String key = "yet.another.key";
523 config.addProperty(key, Boolean.TRUE);
524 assertEquals("Wrong source for key", config, config.getSource(key));
525 }
526
527 /***
528 * Tests the getSource() method when the passed in key refers to multiple
529 * values, which are all defined in the same source configuration.
530 */
531 public void testGetSourceMulti()
532 {
533 setUpSourceTest();
534 final String key = "list.key";
535 config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
536 assertEquals("Wrong source for multi-value property", config
537 .getConfiguration(CHILD1), config.getSource(key));
538 }
539
540 /***
541 * Tests the getSource() method when the passed in key refers to multiple
542 * values defined by different sources. This should cause an exception.
543 */
544 public void testGetSourceMultiSources()
545 {
546 setUpSourceTest();
547 final String key = "list.key";
548 config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
549 config.getConfiguration(CHILD2).addProperty(key, "a,b,c");
550 try
551 {
552 config.getSource(key);
553 fail("Multiple sources not detected!");
554 }
555 catch (IllegalArgumentException iex)
556 {
557
558 }
559 }
560
561 /***
562 * Tests whether escaped list delimiters are treated correctly.
563 */
564 public void testEscapeListDelimiters()
565 {
566 PropertiesConfiguration sub = new PropertiesConfiguration();
567 sub.addProperty("test.pi", "3//,1415");
568 config.addConfiguration(sub);
569 assertEquals("Wrong value", "3,1415", config.getString("test.pi"));
570 }
571
572 /***
573 * Helper method for writing a file.
574 *
575 * @param file the file to be written
576 * @param content the file's content
577 * @throws IOException if an error occurs
578 */
579 private static void writeFile(File file, String content) throws IOException
580 {
581 PrintWriter out = null;
582 try
583 {
584 out = new PrintWriter(new FileWriter(file));
585 out.print(content);
586 }
587 finally
588 {
589 if (out != null)
590 {
591 out.close();
592 }
593 }
594 }
595
596 /***
597 * Helper method for creating a test configuration to be added to the
598 * combined configuration.
599 *
600 * @return the test configuration
601 */
602 private AbstractConfiguration setUpTestConfiguration()
603 {
604 HierarchicalConfiguration config = new HierarchicalConfiguration();
605 config.addProperty(TEST_KEY, Boolean.TRUE);
606 config.addProperty("test.comment", "This is a test");
607 return config;
608 }
609
610 /***
611 * Test event listener class for checking if the expected invalidate events
612 * are fired.
613 */
614 static class CombinedListener implements ConfigurationListener
615 {
616 int invalidateEvents;
617
618 int otherEvents;
619
620 public void configurationChanged(ConfigurationEvent event)
621 {
622 if (event.getType() == CombinedConfiguration.EVENT_COMBINED_INVALIDATE)
623 {
624 invalidateEvents++;
625 }
626 else
627 {
628 otherEvents++;
629 }
630 }
631
632 /***
633 * Checks if the expected number of events was fired.
634 *
635 * @param expectedInvalidate the expected number of invalidate events
636 * @param expectedOthers the expected number of other events
637 */
638 public void checkEvent(int expectedInvalidate, int expectedOthers)
639 {
640 Assert.assertEquals("Wrong number of invalidate events",
641 expectedInvalidate, invalidateEvents);
642 Assert.assertEquals("Wrong number of other events", expectedOthers,
643 otherEvents);
644 }
645 }
646 }