001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.configuration; 018 019 import static org.junit.Assert.assertEquals; 020 import static org.junit.Assert.assertFalse; 021 import static org.junit.Assert.assertNotNull; 022 import static org.junit.Assert.assertNotSame; 023 import static org.junit.Assert.assertNull; 024 import static org.junit.Assert.assertSame; 025 import static org.junit.Assert.assertTrue; 026 027 import java.io.File; 028 import java.io.FileWriter; 029 import java.io.IOException; 030 import java.io.PrintWriter; 031 import java.io.StringReader; 032 import java.io.StringWriter; 033 import java.text.MessageFormat; 034 import java.util.Collection; 035 import java.util.List; 036 import java.util.NoSuchElementException; 037 import java.util.Set; 038 039 import junit.framework.Assert; 040 041 import org.apache.commons.configuration.event.ConfigurationEvent; 042 import org.apache.commons.configuration.event.ConfigurationListener; 043 import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy; 044 import org.apache.commons.configuration.reloading.FileRandomReloadingStrategy; 045 import org.apache.commons.configuration.tree.DefaultExpressionEngine; 046 import org.apache.commons.configuration.tree.MergeCombiner; 047 import org.apache.commons.configuration.tree.NodeCombiner; 048 import org.apache.commons.configuration.tree.OverrideCombiner; 049 import org.apache.commons.configuration.tree.UnionCombiner; 050 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; 051 import org.junit.Before; 052 import org.junit.Rule; 053 import org.junit.Test; 054 import org.junit.rules.TemporaryFolder; 055 056 /** 057 * Test class for CombinedConfiguration. 058 * 059 * @version $Id: TestCombinedConfiguration.java 1327061 2012-04-17 12:18:27Z rgoers $ 060 */ 061 public class TestCombinedConfiguration 062 { 063 /** Constant for the name of a sub configuration. */ 064 private static final String TEST_NAME = "SUBCONFIG"; 065 066 /** Constant for a test key. */ 067 private static final String TEST_KEY = "test.value"; 068 069 /** Constant for the name of the first child configuration.*/ 070 private static final String CHILD1 = TEST_NAME + "1"; 071 072 /** Constant for the name of the second child configuration.*/ 073 private static final String CHILD2 = TEST_NAME + "2"; 074 075 /** Constant for the name of the XML reload test file.*/ 076 private static final String RELOAD_XML_NAME = "reload.xml"; 077 078 /** Constant for the content of a XML reload test file.*/ 079 private static final String RELOAD_XML_CONTENT = "<xml><xmlReload>{0}</xmlReload></xml>"; 080 081 /** Constant for the name of the properties reload test file.*/ 082 private static final String RELOAD_PROPS_NAME = "reload.properties"; 083 084 /** Constant for the content of a properties reload test file.*/ 085 private static final String RELOAD_PROPS_CONTENT = "propsReload = {0}"; 086 087 /** Helper object for managing temporary files. */ 088 @Rule 089 public TemporaryFolder folder = new TemporaryFolder(); 090 091 /** The configuration to be tested. */ 092 private CombinedConfiguration config; 093 094 /** The test event listener. */ 095 private CombinedListener listener; 096 097 @Before 098 public void setUp() throws Exception 099 { 100 config = new CombinedConfiguration(); 101 listener = new CombinedListener(); 102 config.addConfigurationListener(listener); 103 } 104 105 /** 106 * Tests accessing a newly created combined configuration. 107 */ 108 @Test 109 public void testInit() 110 { 111 assertEquals("Already configurations contained", 0, config 112 .getNumberOfConfigurations()); 113 assertTrue("Set of names is not empty", config.getConfigurationNames() 114 .isEmpty()); 115 assertTrue("Wrong node combiner", 116 config.getNodeCombiner() instanceof UnionCombiner); 117 assertNull("Test config was found", config.getConfiguration(TEST_NAME)); 118 assertFalse("Force reload check flag is set", config.isForceReloadCheck()); 119 } 120 121 /** 122 * Tests adding a configuration (without further information). 123 */ 124 @Test 125 public void testAddConfiguration() 126 { 127 AbstractConfiguration c = setUpTestConfiguration(); 128 config.addConfiguration(c); 129 checkAddConfig(c); 130 assertEquals("Wrong number of configs", 1, config 131 .getNumberOfConfigurations()); 132 assertTrue("Name list is not empty", config.getConfigurationNames() 133 .isEmpty()); 134 assertSame("Added config not found", c, config.getConfiguration(0)); 135 assertTrue("Wrong property value", config.getBoolean(TEST_KEY)); 136 listener.checkEvent(1, 0); 137 } 138 139 /** 140 * Tests adding a configuration with a name. 141 */ 142 @Test 143 public void testAddConfigurationWithName() 144 { 145 AbstractConfiguration c = setUpTestConfiguration(); 146 config.addConfiguration(c, TEST_NAME); 147 checkAddConfig(c); 148 assertEquals("Wrong number of configs", 1, config 149 .getNumberOfConfigurations()); 150 assertSame("Added config not found", c, config.getConfiguration(0)); 151 assertSame("Added config not found by name", c, config 152 .getConfiguration(TEST_NAME)); 153 Set<String> names = config.getConfigurationNames(); 154 assertEquals("Wrong number of config names", 1, names.size()); 155 assertTrue("Name not found", names.contains(TEST_NAME)); 156 assertTrue("Wrong property value", config.getBoolean(TEST_KEY)); 157 listener.checkEvent(1, 0); 158 } 159 160 /** 161 * Tests adding a configuration with a name when this name already exists. 162 * This should cause an exception. 163 */ 164 @Test(expected = ConfigurationRuntimeException.class) 165 public void testAddConfigurationWithNameTwice() 166 { 167 config.addConfiguration(setUpTestConfiguration(), TEST_NAME); 168 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, 169 "prefix"); 170 } 171 172 /** 173 * Tests adding a configuration and specifying an at position. 174 */ 175 @Test 176 public void testAddConfigurationAt() 177 { 178 AbstractConfiguration c = setUpTestConfiguration(); 179 config.addConfiguration(c, null, "my"); 180 checkAddConfig(c); 181 assertTrue("Wrong property value", config.getBoolean("my." + TEST_KEY)); 182 } 183 184 /** 185 * Tests adding a configuration with a complex at position. Here the at path 186 * contains a dot, which must be escaped. 187 */ 188 @Test 189 public void testAddConfigurationComplexAt() 190 { 191 AbstractConfiguration c = setUpTestConfiguration(); 192 config.addConfiguration(c, null, "This..is.a.complex"); 193 checkAddConfig(c); 194 assertTrue("Wrong property value", config 195 .getBoolean("This..is.a.complex." + TEST_KEY)); 196 } 197 198 /** 199 * Checks if a configuration was correctly added to the combined config. 200 * 201 * @param c the config to check 202 */ 203 private void checkAddConfig(AbstractConfiguration c) 204 { 205 Collection<ConfigurationListener> listeners = c.getConfigurationListeners(); 206 assertEquals("Wrong number of configuration listeners", 1, listeners 207 .size()); 208 assertTrue("Combined config is no listener", listeners.contains(config)); 209 } 210 211 /** 212 * Tests adding a null configuration. This should cause an exception to be 213 * thrown. 214 */ 215 @Test(expected = IllegalArgumentException.class) 216 public void testAddNullConfiguration() 217 { 218 config.addConfiguration(null); 219 } 220 221 /** 222 * Tests accessing properties if no configurations have been added. 223 */ 224 @Test 225 public void testAccessPropertyEmpty() 226 { 227 assertFalse("Found a key", config.containsKey(TEST_KEY)); 228 assertNull("Key has a value", config.getString("test.comment")); 229 assertTrue("Config is not empty", config.isEmpty()); 230 } 231 232 /** 233 * Tests accessing properties if multiple configurations have been added. 234 */ 235 @Test 236 public void testAccessPropertyMulti() 237 { 238 config.addConfiguration(setUpTestConfiguration()); 239 config.addConfiguration(setUpTestConfiguration(), null, "prefix1"); 240 config.addConfiguration(setUpTestConfiguration(), null, "prefix2"); 241 assertTrue("Prop1 not found", config.getBoolean(TEST_KEY)); 242 assertTrue("Prop 2 not found", config.getBoolean("prefix1." + TEST_KEY)); 243 assertTrue("Prop 3 not found", config.getBoolean("prefix2." + TEST_KEY)); 244 assertFalse("Configuration is empty", config.isEmpty()); 245 listener.checkEvent(3, 0); 246 } 247 248 /** 249 * Tests removing a configuration. 250 */ 251 @Test 252 public void testRemoveConfiguration() 253 { 254 AbstractConfiguration c = setUpTestConfiguration(); 255 config.addConfiguration(c); 256 checkAddConfig(c); 257 assertTrue("Config could not be removed", config.removeConfiguration(c)); 258 checkRemoveConfig(c); 259 } 260 261 /** 262 * Tests removing a configuration by index. 263 */ 264 @Test 265 public void testRemoveConfigurationAt() 266 { 267 AbstractConfiguration c = setUpTestConfiguration(); 268 config.addConfiguration(c); 269 assertSame("Wrong config removed", c, config.removeConfigurationAt(0)); 270 checkRemoveConfig(c); 271 } 272 273 /** 274 * Tests removing a configuration by name. 275 */ 276 @Test 277 public void testRemoveConfigurationByName() 278 { 279 AbstractConfiguration c = setUpTestConfiguration(); 280 config.addConfiguration(c, TEST_NAME); 281 assertSame("Wrong config removed", c, config 282 .removeConfiguration(TEST_NAME)); 283 checkRemoveConfig(c); 284 } 285 286 /** 287 * Tests removing a configuration with a name. 288 */ 289 @Test 290 public void testRemoveNamedConfiguration() 291 { 292 AbstractConfiguration c = setUpTestConfiguration(); 293 config.addConfiguration(c, TEST_NAME); 294 config.removeConfiguration(c); 295 checkRemoveConfig(c); 296 } 297 298 /** 299 * Tests removing a named configuration by index. 300 */ 301 @Test 302 public void testRemoveNamedConfigurationAt() 303 { 304 AbstractConfiguration c = setUpTestConfiguration(); 305 config.addConfiguration(c, TEST_NAME); 306 assertSame("Wrong config removed", c, config.removeConfigurationAt(0)); 307 checkRemoveConfig(c); 308 } 309 310 /** 311 * Tests removing a configuration that was not added prior. 312 */ 313 @Test 314 public void testRemoveNonContainedConfiguration() 315 { 316 assertFalse("Could remove non contained config", config 317 .removeConfiguration(setUpTestConfiguration())); 318 listener.checkEvent(0, 0); 319 } 320 321 /** 322 * Tests removing a configuration by name, which is not contained. 323 */ 324 @Test 325 public void testRemoveConfigurationByUnknownName() 326 { 327 assertNull("Could remove configuration by unknown name", config 328 .removeConfiguration("unknownName")); 329 listener.checkEvent(0, 0); 330 } 331 332 /** 333 * Tests whether a configuration was completely removed. 334 * 335 * @param c the removed configuration 336 */ 337 private void checkRemoveConfig(AbstractConfiguration c) 338 { 339 assertTrue("Listener was not removed", c.getConfigurationListeners() 340 .isEmpty()); 341 assertEquals("Wrong number of contained configs", 0, config 342 .getNumberOfConfigurations()); 343 assertTrue("Name was not removed", config.getConfigurationNames() 344 .isEmpty()); 345 listener.checkEvent(2, 0); 346 } 347 348 /** 349 * Tests if an update of a contained configuration leeds to an invalidation 350 * of the combined configuration. 351 */ 352 @Test 353 public void testUpdateContainedConfiguration() 354 { 355 AbstractConfiguration c = setUpTestConfiguration(); 356 config.addConfiguration(c); 357 c.addProperty("test.otherTest", "yes"); 358 assertEquals("New property not found", "yes", config 359 .getString("test.otherTest")); 360 listener.checkEvent(2, 0); 361 } 362 363 /** 364 * Tests if setting a node combiner causes an invalidation. 365 */ 366 @Test 367 public void testSetNodeCombiner() 368 { 369 NodeCombiner combiner = new UnionCombiner(); 370 config.setNodeCombiner(combiner); 371 assertSame("Node combiner was not set", combiner, config 372 .getNodeCombiner()); 373 listener.checkEvent(1, 0); 374 } 375 376 /** 377 * Tests setting a null node combiner. This should cause an exception. 378 */ 379 @Test(expected = IllegalArgumentException.class) 380 public void testSetNullNodeCombiner() 381 { 382 config.setNodeCombiner(null); 383 } 384 385 /** 386 * Tests cloning a combined configuration. 387 */ 388 @Test 389 public void testClone() 390 { 391 config.addConfiguration(setUpTestConfiguration()); 392 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2"); 393 config.addConfiguration(new PropertiesConfiguration(), "props"); 394 395 CombinedConfiguration cc2 = (CombinedConfiguration) config.clone(); 396 assertEquals("Wrong number of contained configurations", config 397 .getNumberOfConfigurations(), cc2.getNumberOfConfigurations()); 398 assertSame("Wrong node combiner", config.getNodeCombiner(), cc2 399 .getNodeCombiner()); 400 assertEquals("Wrong number of names", config.getConfigurationNames() 401 .size(), cc2.getConfigurationNames().size()); 402 assertTrue("Event listeners were cloned", cc2 403 .getConfigurationListeners().isEmpty()); 404 405 StrictConfigurationComparator comp = new StrictConfigurationComparator(); 406 for (int i = 0; i < config.getNumberOfConfigurations(); i++) 407 { 408 assertNotSame("Configuration at " + i + " was not cloned", config 409 .getConfiguration(i), cc2.getConfiguration(i)); 410 assertEquals("Wrong config class at " + i, config.getConfiguration( 411 i).getClass(), cc2.getConfiguration(i).getClass()); 412 assertTrue("Configs not equal at " + i, comp.compare(config 413 .getConfiguration(i), cc2.getConfiguration(i))); 414 } 415 416 assertTrue("Combined configs not equal", comp.compare(config, cc2)); 417 } 418 419 /** 420 * Tests if the cloned configuration is decoupled from the original. 421 */ 422 @Test 423 public void testCloneModify() 424 { 425 config.addConfiguration(setUpTestConfiguration(), TEST_NAME); 426 CombinedConfiguration cc2 = (CombinedConfiguration) config.clone(); 427 assertTrue("Name is missing", cc2.getConfigurationNames().contains( 428 TEST_NAME)); 429 cc2.removeConfiguration(TEST_NAME); 430 assertFalse("Names in original changed", config.getConfigurationNames() 431 .isEmpty()); 432 } 433 434 /** 435 * Tests clearing a combined configuration. This should remove all contained 436 * configurations. 437 */ 438 @Test 439 public void testClear() 440 { 441 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "test"); 442 config.addConfiguration(setUpTestConfiguration()); 443 444 config.clear(); 445 assertEquals("Still configs contained", 0, config 446 .getNumberOfConfigurations()); 447 assertTrue("Still names contained", config.getConfigurationNames() 448 .isEmpty()); 449 assertTrue("Config is not empty", config.isEmpty()); 450 451 listener.checkEvent(3, 2); 452 } 453 454 /** 455 * Tests if file-based configurations can be reloaded. 456 */ 457 @Test 458 public void testReloading() throws Exception 459 { 460 config.setForceReloadCheck(true); 461 File testXmlFile = writeReloadFile(RELOAD_XML_NAME, RELOAD_XML_CONTENT, 0); 462 File testPropsFile = writeReloadFile(RELOAD_PROPS_NAME, RELOAD_PROPS_CONTENT, 0); 463 XMLConfiguration c1 = new XMLConfiguration(testXmlFile); 464 c1.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 465 PropertiesConfiguration c2 = new PropertiesConfiguration(testPropsFile); 466 c2.setThrowExceptionOnMissing(true); 467 c2.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 468 config.addConfiguration(c1); 469 config.addConfiguration(c2); 470 assertEquals("Wrong xml reload value", 0, config.getInt("xmlReload")); 471 assertEquals("Wrong props reload value", 0, config 472 .getInt("propsReload")); 473 474 writeReloadFile(RELOAD_XML_NAME, RELOAD_XML_CONTENT, 1); 475 assertEquals("XML reload not detected", 1, config.getInt("xmlReload")); 476 config.setForceReloadCheck(false); 477 writeReloadFile(RELOAD_PROPS_NAME, RELOAD_PROPS_CONTENT, 1); 478 assertEquals("Props reload detected though check flag is false", 0, config 479 .getInt("propsReload")); 480 } 481 482 /** 483 * Tests whether the reload check works with a subnode configuration. This 484 * test is related to CONFIGURATION-341. 485 */ 486 @Test 487 public void testReloadingSubnodeConfig() throws IOException, 488 ConfigurationException 489 { 490 config.setForceReloadCheck(true); 491 File testXmlFile = writeReloadFile(RELOAD_XML_NAME, RELOAD_XML_CONTENT, 492 0); 493 XMLConfiguration c1 = new XMLConfiguration(testXmlFile); 494 c1.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 495 final String prefix = "reloadCheck"; 496 config.addConfiguration(c1, CHILD1, prefix); 497 SubnodeConfiguration sub = config.configurationAt(prefix, true); 498 writeReloadFile(RELOAD_XML_NAME, RELOAD_XML_CONTENT, 1); 499 assertEquals("Reload not detected", 1, sub.getInt("xmlReload")); 500 } 501 502 /** 503 * Tests whether reloading works for a combined configuration nested in 504 * another combined configuration. 505 */ 506 @Test 507 public void testReloadingNestedCC() throws IOException, 508 ConfigurationException 509 { 510 config.setForceReloadCheck(true); 511 File testXmlFile = 512 writeReloadFile(RELOAD_XML_NAME, RELOAD_XML_CONTENT, 0); 513 File testPropsFile = 514 writeReloadFile(RELOAD_PROPS_NAME, RELOAD_PROPS_CONTENT, 0); 515 XMLConfiguration c1 = new XMLConfiguration(testXmlFile); 516 c1.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 517 PropertiesConfiguration c2 = new PropertiesConfiguration(testPropsFile); 518 c2.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 519 config.addConfiguration(c2); 520 CombinedConfiguration cc2 = new CombinedConfiguration(); 521 cc2.setForceReloadCheck(true); 522 cc2.addConfiguration(c1); 523 config.addConfiguration(cc2); 524 assertEquals("Wrong xml reload value", 0, config.getInt("xmlReload")); 525 writeReloadFile(RELOAD_XML_NAME, RELOAD_XML_CONTENT, 1); 526 assertEquals("XML reload not detected", 1, config.getInt("xmlReload")); 527 } 528 529 /** 530 * Prepares a test of the getSource() method. 531 */ 532 private void setUpSourceTest() 533 { 534 HierarchicalConfiguration c1 = new HierarchicalConfiguration(); 535 PropertiesConfiguration c2 = new PropertiesConfiguration(); 536 c1.addProperty(TEST_KEY, TEST_NAME); 537 c2.addProperty("another.key", "test"); 538 config.addConfiguration(c1, CHILD1); 539 config.addConfiguration(c2, CHILD2); 540 } 541 542 /** 543 * Tests the gestSource() method when the source property is defined in a 544 * hierarchical configuration. 545 */ 546 @Test 547 public void testGetSourceHierarchical() 548 { 549 setUpSourceTest(); 550 assertEquals("Wrong source configuration", config 551 .getConfiguration(CHILD1), config.getSource(TEST_KEY)); 552 } 553 554 /** 555 * Tests whether the source configuration can be detected for non 556 * hierarchical configurations. 557 */ 558 @Test 559 public void testGetSourceNonHierarchical() 560 { 561 setUpSourceTest(); 562 assertEquals("Wrong source configuration", config 563 .getConfiguration(CHILD2), config.getSource("another.key")); 564 } 565 566 /** 567 * Tests the getSource() method when the passed in key is not contained. 568 * Result should be null in this case. 569 */ 570 @Test 571 public void testGetSourceUnknown() 572 { 573 setUpSourceTest(); 574 assertNull("Wrong result for unknown key", config 575 .getSource("an.unknown.key")); 576 } 577 578 /** 579 * Tests the getSource() method when a null key is passed in. This should 580 * cause an exception. 581 */ 582 @Test(expected = IllegalArgumentException.class) 583 public void testGetSourceNull() 584 { 585 config.getSource(null); 586 } 587 588 /** 589 * Tests the getSource() method when the passed in key belongs to the 590 * combined configuration itself. 591 */ 592 @Test 593 public void testGetSourceCombined() 594 { 595 setUpSourceTest(); 596 final String key = "yet.another.key"; 597 config.addProperty(key, Boolean.TRUE); 598 assertEquals("Wrong source for key", config, config.getSource(key)); 599 } 600 601 /** 602 * Tests the getSource() method when the passed in key refers to multiple 603 * values, which are all defined in the same source configuration. 604 */ 605 @Test 606 public void testGetSourceMulti() 607 { 608 setUpSourceTest(); 609 final String key = "list.key"; 610 config.getConfiguration(CHILD1).addProperty(key, "1,2,3"); 611 assertEquals("Wrong source for multi-value property", config 612 .getConfiguration(CHILD1), config.getSource(key)); 613 } 614 615 /** 616 * Tests the getSource() method when the passed in key refers to multiple 617 * values defined by different sources. This should cause an exception. 618 */ 619 @Test(expected = IllegalArgumentException.class) 620 public void testGetSourceMultiSources() 621 { 622 setUpSourceTest(); 623 final String key = "list.key"; 624 config.getConfiguration(CHILD1).addProperty(key, "1,2,3"); 625 config.getConfiguration(CHILD2).addProperty(key, "a,b,c"); 626 config.getSource(key); 627 } 628 629 /** 630 * Tests whether escaped list delimiters are treated correctly. 631 */ 632 @Test 633 public void testEscapeListDelimiters() 634 { 635 PropertiesConfiguration sub = new PropertiesConfiguration(); 636 sub.addProperty("test.pi", "3\\,1415"); 637 config.addConfiguration(sub); 638 assertEquals("Wrong value", "3,1415", config.getString("test.pi")); 639 } 640 641 /** 642 * Tests whether an invalidate event is fired only after a change. This test 643 * is related to CONFIGURATION-315. 644 */ 645 @Test 646 public void testInvalidateAfterChange() 647 { 648 ConfigurationEvent event = new ConfigurationEvent(config, 0, null, 649 null, true); 650 config.configurationChanged(event); 651 assertEquals("Invalidate event fired", 0, listener.invalidateEvents); 652 event = new ConfigurationEvent(config, 0, null, null, false); 653 config.configurationChanged(event); 654 assertEquals("No invalidate event fired", 1, listener.invalidateEvents); 655 } 656 657 /** 658 * Tests using a conversion expression engine for child configurations with 659 * strange keys. This test is related to CONFIGURATION-336. 660 */ 661 @Test 662 public void testConversionExpressionEngine() 663 { 664 PropertiesConfiguration child = new PropertiesConfiguration(); 665 child.addProperty("test(a)", "1,2,3"); 666 config.addConfiguration(child); 667 DefaultExpressionEngine engineQuery = new DefaultExpressionEngine(); 668 engineQuery.setIndexStart("<"); 669 engineQuery.setIndexEnd(">"); 670 config.setExpressionEngine(engineQuery); 671 DefaultExpressionEngine engineConvert = new DefaultExpressionEngine(); 672 engineConvert.setIndexStart("["); 673 engineConvert.setIndexEnd("]"); 674 config.setConversionExpressionEngine(engineConvert); 675 assertEquals("Wrong property 1", "1", config.getString("test(a)<0>")); 676 assertEquals("Wrong property 2", "2", config.getString("test(a)<1>")); 677 assertEquals("Wrong property 3", "3", config.getString("test(a)<2>")); 678 } 679 680 /** 681 * Tests whether reload operations can cause a deadlock when the combined 682 * configuration is accessed concurrently. This test is related to 683 * CONFIGURATION-344. 684 */ 685 @Test 686 public void testDeadlockWithReload() throws ConfigurationException, 687 InterruptedException 688 { 689 final PropertiesConfiguration child = new PropertiesConfiguration( 690 "test.properties"); 691 child.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 692 config.addConfiguration(child); 693 final int count = 1000; 694 695 class TestDeadlockReloadThread extends Thread 696 { 697 boolean error = false; 698 699 @Override 700 public void run() 701 { 702 for (int i = 0; i < count && !error; i++) 703 { 704 try 705 { 706 if (!child.getBoolean("configuration.loaded")) 707 { 708 error = true; 709 } 710 } 711 catch (NoSuchElementException nsex) 712 { 713 error = true; 714 } 715 } 716 } 717 } 718 719 TestDeadlockReloadThread reloadThread = new TestDeadlockReloadThread(); 720 reloadThread.start(); 721 for (int i = 0; i < count; i++) 722 { 723 assertEquals("Wrong value of combined property", 10, config 724 .getInt("test.integer")); 725 } 726 reloadThread.join(); 727 assertFalse("Failure in thread", reloadThread.error); 728 } 729 730 @Test 731 public void testGetConfigurations() throws Exception 732 { 733 config.addConfiguration(setUpTestConfiguration()); 734 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2"); 735 AbstractConfiguration pc = new PropertiesConfiguration(); 736 config.addConfiguration(pc, "props"); 737 List<AbstractConfiguration> list = config.getConfigurations(); 738 assertNotNull("No list of configurations returned", list); 739 assertTrue("Incorrect number of configurations", list.size() == 3); 740 AbstractConfiguration c = list.get(2); 741 assertTrue("Incorrect configuration", c == pc); 742 } 743 744 @Test 745 public void testGetConfigurationNameList() throws Exception 746 { 747 config.addConfiguration(setUpTestConfiguration()); 748 config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2"); 749 AbstractConfiguration pc = new PropertiesConfiguration(); 750 config.addConfiguration(pc, "props"); 751 List<String> list = config.getConfigurationNameList(); 752 assertNotNull("No list of configurations returned", list); 753 assertTrue("Incorrect number of configurations", list.size() == 3); 754 String name = list.get(1); 755 assertNotNull("No name returned", name); 756 assertTrue("Incorrect configuration name", TEST_NAME.equals(name)); 757 } 758 759 /** 760 * Tests whether changes on a sub node configuration that is part of a 761 * combined configuration are detected. This test is related to 762 * CONFIGURATION-368. 763 */ 764 @Test 765 public void testReloadWithSubNodeConfig() throws Exception 766 { 767 final String reloadContent = "<config><default><xmlReload1>{0}</xmlReload1></default></config>"; 768 config.setForceReloadCheck(true); 769 config.setNodeCombiner(new OverrideCombiner()); 770 File testXmlFile1 = writeReloadFile(RELOAD_XML_NAME, reloadContent, 0); 771 final String prefix1 = "default"; 772 XMLConfiguration c1 = new XMLConfiguration(testXmlFile1); 773 SubnodeConfiguration sub1 = c1.configurationAt(prefix1, true); 774 assertEquals("Inital test for sub config 1 failed", 0, sub1 775 .getInt("xmlReload1")); 776 config.addConfiguration(sub1); 777 assertEquals( 778 "Could not get value for sub config 1 from combined config", 0, 779 config.getInt("xmlReload1")); 780 c1.setReloadingStrategy(new FileAlwaysReloadingStrategy()); 781 writeReloadFile(RELOAD_XML_NAME, reloadContent, 1); 782 assertEquals("Reload of sub config 1 not detected", 1, config 783 .getInt("xmlReload1")); 784 } 785 786 @Test 787 public void testConcurrentGetAndReload() throws Exception 788 { 789 final int threadCount = 5; 790 final int loopCount = 1000; 791 config.setForceReloadCheck(true); 792 config.setNodeCombiner(new MergeCombiner()); 793 final XMLConfiguration xml = new XMLConfiguration("configA.xml"); 794 xml.setReloadingStrategy(new FileRandomReloadingStrategy()); 795 config.addConfiguration(xml); 796 final XMLConfiguration xml2 = new XMLConfiguration("configB.xml"); 797 xml2.setReloadingStrategy(new FileRandomReloadingStrategy()); 798 config.addConfiguration(xml2); 799 config.setExpressionEngine(new XPathExpressionEngine()); 800 801 assertEquals(config.getString("/property[@name='config']/@value"), "100"); 802 803 Thread testThreads[] = new Thread[threadCount]; 804 int failures[] = new int[threadCount]; 805 806 for (int i = 0; i < testThreads.length; ++i) 807 { 808 testThreads[i] = new ReloadThread(config, failures, i, loopCount); 809 testThreads[i].start(); 810 } 811 812 int totalFailures = 0; 813 for (int i = 0; i < testThreads.length; ++i) 814 { 815 testThreads[i].join(); 816 totalFailures += failures[i]; 817 } 818 assertTrue(totalFailures + " failures Occurred", totalFailures == 0); 819 } 820 821 /** 822 * Tests whether a combined configuration can be copied to an XML 823 * configuration. This test is related to CONFIGURATION-445. 824 */ 825 @Test 826 public void testCombinedCopyToXML() throws ConfigurationException 827 { 828 XMLConfiguration x1 = new XMLConfiguration(); 829 x1.addProperty("key1", "value1"); 830 x1.addProperty("key1[@override]", "USER1"); 831 x1.addProperty("key2", "value2"); 832 x1.addProperty("key2[@override]", "USER2"); 833 XMLConfiguration x2 = new XMLConfiguration(); 834 x2.addProperty("key2", "value2.2"); 835 x2.addProperty("key2[@override]", "USER2"); 836 config.setNodeCombiner(new OverrideCombiner()); 837 config.addConfiguration(x2); 838 config.addConfiguration(x1); 839 XMLConfiguration x3 = new XMLConfiguration(config); 840 assertEquals("Wrong element value", "value2.2", x3.getString("key2")); 841 assertEquals("Wrong attribute value", "USER2", 842 x3.getString("key2[@override]")); 843 StringWriter w = new StringWriter(); 844 x3.save(w); 845 String s = w.toString(); 846 x3 = new XMLConfiguration(); 847 x3.load(new StringReader(s)); 848 assertEquals("Wrong element value after load", "value2.2", 849 x3.getString("key2")); 850 assertEquals("Wrong attribute value after load", "USER2", 851 x3.getString("key2[@override]")); 852 } 853 854 private class ReloadThread extends Thread 855 { 856 CombinedConfiguration combined; 857 int[] failures; 858 int index; 859 int count; 860 861 ReloadThread(CombinedConfiguration config, int[] failures, int index, int count) 862 { 863 combined = config; 864 this.failures = failures; 865 this.index = index; 866 this.count = count; 867 } 868 @Override 869 public void run() 870 { 871 failures[index] = 0; 872 for (int i = 0; i < count; i++) 873 { 874 try 875 { 876 String value = combined.getString("/property[@name='config']/@value"); 877 if (value == null || !value.equals("100")) 878 { 879 ++failures[index]; 880 } 881 } 882 catch (Exception ex) 883 { 884 ++failures[index]; 885 } 886 } 887 } 888 } 889 890 /** 891 * Helper method for writing a file. The file is also added to a list and 892 * will be deleted in teadDown() automatically. 893 * 894 * @param file the file to be written 895 * @param content the file's content 896 * @throws IOException if an error occurs 897 */ 898 private void writeFile(File file, String content) throws IOException 899 { 900 PrintWriter out = null; 901 try 902 { 903 out = new PrintWriter(new FileWriter(file)); 904 out.print(content); 905 } 906 finally 907 { 908 if (out != null) 909 { 910 out.close(); 911 } 912 } 913 } 914 915 /** 916 * Helper method for writing a test file. The file will be created in the 917 * test directory. It is also scheduled for automatic deletion after the 918 * test. 919 * 920 * @param fileName the name of the test file 921 * @param content the content of the file 922 * @return the <code>File</code> object for the test file 923 * @throws IOException if an error occurs 924 */ 925 private File writeFile(String fileName, String content) throws IOException 926 { 927 File file = new File(folder.getRoot(), fileName); 928 writeFile(file, content); 929 return file; 930 } 931 932 /** 933 * Writes a file for testing reload operations. 934 * 935 * @param name the name of the reload test file 936 * @param content the content of the file 937 * @param value the value of the reload test property 938 * @return the file that was written 939 * @throws IOException if an error occurs 940 */ 941 private File writeReloadFile(String name, String content, int value) 942 throws IOException 943 { 944 return writeFile(name, MessageFormat.format(content, new Object[] { 945 new Integer(value) 946 })); 947 } 948 949 /** 950 * Helper method for creating a test configuration to be added to the 951 * combined configuration. 952 * 953 * @return the test configuration 954 */ 955 private AbstractConfiguration setUpTestConfiguration() 956 { 957 HierarchicalConfiguration config = new HierarchicalConfiguration(); 958 config.addProperty(TEST_KEY, Boolean.TRUE); 959 config.addProperty("test.comment", "This is a test"); 960 return config; 961 } 962 963 /** 964 * Test event listener class for checking if the expected invalidate events 965 * are fired. 966 */ 967 static class CombinedListener implements ConfigurationListener 968 { 969 int invalidateEvents; 970 971 int otherEvents; 972 973 public void configurationChanged(ConfigurationEvent event) 974 { 975 if (event.getType() == CombinedConfiguration.EVENT_COMBINED_INVALIDATE) 976 { 977 invalidateEvents++; 978 } 979 else 980 { 981 otherEvents++; 982 } 983 } 984 985 /** 986 * Checks if the expected number of events was fired. 987 * 988 * @param expectedInvalidate the expected number of invalidate events 989 * @param expectedOthers the expected number of other events 990 */ 991 public void checkEvent(int expectedInvalidate, int expectedOthers) 992 { 993 Assert.assertEquals("Wrong number of invalidate events", 994 expectedInvalidate, invalidateEvents); 995 Assert.assertEquals("Wrong number of other events", expectedOthers, 996 otherEvents); 997 } 998 } 999 }