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.assertNull;
023 import static org.junit.Assert.assertSame;
024 import static org.junit.Assert.assertTrue;
025 import static org.junit.Assert.fail;
026
027 import java.io.File;
028 import java.io.StringWriter;
029 import java.lang.reflect.InvocationHandler;
030 import java.lang.reflect.Method;
031 import java.lang.reflect.Proxy;
032 import java.net.URL;
033 import java.util.Collection;
034 import java.util.HashMap;
035 import java.util.Iterator;
036 import java.util.List;
037 import java.util.Map;
038 import java.util.Set;
039
040 import org.apache.commons.configuration.beanutils.BeanHelper;
041 import org.apache.commons.configuration.event.ConfigurationListenerTestImpl;
042 import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
043 import org.apache.commons.configuration.tree.ConfigurationNode;
044 import org.apache.commons.configuration.tree.DefaultConfigurationNode;
045 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
046 import org.apache.commons.lang.SystemUtils;
047 import org.apache.commons.lang.text.StrLookup;
048 import org.apache.commons.logging.Log;
049 import org.apache.commons.logging.LogFactory;
050 import org.apache.commons.logging.impl.Log4JLogger;
051 import org.apache.log4j.Level;
052 import org.apache.log4j.Logger;
053 import org.apache.log4j.SimpleLayout;
054 import org.apache.log4j.WriterAppender;
055 import org.junit.Before;
056 import org.junit.Test;
057
058 /**
059 * Test class for DefaultConfigurationBuilder.
060 *
061 * @author <a
062 * href="http://commons.apache.org/configuration/team-list.html">Commons
063 * Configuration team</a>
064 * @version $Id: TestDefaultConfigurationBuilder.java 1301997 2012-03-17 20:32:02Z sebb $
065 */
066 public class TestDefaultConfigurationBuilder
067 {
068 /** Test configuration definition file. */
069 private static final File TEST_FILE = ConfigurationAssert
070 .getTestFile("testDigesterConfiguration.xml");
071
072 private static final File ADDITIONAL_FILE = ConfigurationAssert
073 .getTestFile("testDigesterConfiguration2.xml");
074
075 private static final File OPTIONAL_FILE = ConfigurationAssert
076 .getTestFile("testDigesterOptionalConfiguration.xml");
077
078 private static final File OPTIONALEX_FILE = ConfigurationAssert
079 .getTestFile("testDigesterOptionalConfigurationEx.xml");
080
081 private static final File MULTI_FILE = ConfigurationAssert
082 .getTestFile("testDigesterConfiguration3.xml");
083
084 private static final File INIT_FILE = ConfigurationAssert
085 .getTestFile("testComplexInitialization.xml");
086
087 private static final File CLASS_FILE = ConfigurationAssert
088 .getTestFile("testExtendedClass.xml");
089
090 private static final File PROVIDER_FILE = ConfigurationAssert
091 .getTestFile("testConfigurationProvider.xml");
092
093 private static final File EXTENDED_PROVIDER_FILE = ConfigurationAssert
094 .getTestFile("testExtendedXMLConfigurationProvider.xml");
095
096 private static final File GLOBAL_LOOKUP_FILE = ConfigurationAssert
097 .getTestFile("testGlobalLookup.xml");
098
099 private static final File SYSTEM_PROPS_FILE = ConfigurationAssert
100 .getTestFile("testSystemProperties.xml");
101
102 private static final File VALIDATION_FILE = ConfigurationAssert
103 .getTestFile("testValidation.xml");
104
105 private static final File VALIDATION3_FILE = ConfigurationAssert
106 .getTestFile("testValidation3.xml");
107
108 private static final File MULTI_TENENT_FILE = ConfigurationAssert
109 .getTestFile("testMultiTenentConfigurationBuilder.xml");
110
111 private static final File EXPRESSION_FILE = ConfigurationAssert
112 .getTestFile("testExpression.xml");
113
114 /** Constant for the name of an optional configuration.*/
115 private static final String OPTIONAL_NAME = "optionalConfig";
116
117 /** Stores the object to be tested. */
118 DefaultConfigurationBuilder factory;
119
120 @Before
121 public void setUp() throws Exception
122 {
123 System
124 .setProperty("java.naming.factory.initial",
125 "org.apache.commons.configuration.MockInitialContextFactory");
126 System.setProperty("test_file_xml", "test.xml");
127 System.setProperty("test_file_combine", "testcombine1.xml");
128 factory = new DefaultConfigurationBuilder();
129 factory.clearErrorListeners(); // avoid exception messages
130 }
131
132 /**
133 * Tests the isReservedNode() method of ConfigurationDeclaration.
134 */
135 @Test
136 public void testConfigurationDeclarationIsReserved()
137 {
138 DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
139 factory, factory);
140 DefaultConfigurationNode parent = new DefaultConfigurationNode();
141 DefaultConfigurationNode nd = new DefaultConfigurationNode("at");
142 parent.addAttribute(nd);
143 assertTrue("Attribute at not recognized", decl.isReservedNode(nd));
144 nd = new DefaultConfigurationNode("optional");
145 parent.addAttribute(nd);
146 assertTrue("Attribute optional not recognized", decl.isReservedNode(nd));
147 nd = new DefaultConfigurationNode("config-class");
148 parent.addAttribute(nd);
149 assertTrue("Inherited attribute not recognized", decl
150 .isReservedNode(nd));
151 nd = new DefaultConfigurationNode("different");
152 parent.addAttribute(nd);
153 assertFalse("Wrong reserved attribute", decl.isReservedNode(nd));
154 nd = new DefaultConfigurationNode("at");
155 parent.addChild(nd);
156 assertFalse("Node type not evaluated", decl.isReservedNode(nd));
157 }
158
159 /**
160 * Tests if the at attribute is correctly detected as reserved attribute.
161 */
162 @Test
163 public void testConfigurationDeclarationIsReservedAt()
164 {
165 checkOldReservedAttribute("at");
166 }
167
168 /**
169 * Tests if the optional attribute is correctly detected as reserved
170 * attribute.
171 */
172 @Test
173 public void testConfigurationDeclarationIsReservedOptional()
174 {
175 checkOldReservedAttribute("optional");
176 }
177
178 /**
179 * Tests if special reserved attributes are recognized by the
180 * isReservedNode() method. For compatibility reasons the attributes "at"
181 * and "optional" are also treated as reserved attributes, but only if there
182 * are no corresponding attributes with the "config-" prefix.
183 *
184 * @param name the attribute name
185 */
186 private void checkOldReservedAttribute(String name)
187 {
188 DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
189 factory, factory);
190 DefaultConfigurationNode parent = new DefaultConfigurationNode();
191 DefaultConfigurationNode nd = new DefaultConfigurationNode("config-"
192 + name);
193 parent.addAttribute(nd);
194 assertTrue("config-" + name + " attribute not recognized", decl
195 .isReservedNode(nd));
196 DefaultConfigurationNode nd2 = new DefaultConfigurationNode(name);
197 parent.addAttribute(nd2);
198 assertFalse(name + " is reserved though config- exists", decl
199 .isReservedNode(nd2));
200 assertTrue("config- attribute not recognized when " + name + " exists",
201 decl.isReservedNode(nd));
202 }
203
204 /**
205 * Tests access to certain reserved attributes of a
206 * ConfigurationDeclaration.
207 */
208 public void testConfigurationDeclarationGetAttributes()
209 {
210 factory.addProperty("xml.fileName", "test.xml");
211 DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
212 factory, factory.configurationAt("xml"));
213 assertNull("Found an at attribute", decl.getAt());
214 assertFalse("Found an optional attribute", decl.isOptional());
215 factory.addProperty("xml[@config-at]", "test1");
216 assertEquals("Wrong value of at attribute", "test1", decl.getAt());
217 factory.addProperty("xml[@at]", "test2");
218 assertEquals("Wrong value of config-at attribute", "test1", decl.getAt());
219 factory.clearProperty("xml[@config-at]");
220 assertEquals("Old at attribute not detected", "test2", decl.getAt());
221 factory.addProperty("xml[@config-optional]", "true");
222 assertTrue("Wrong value of optional attribute", decl.isOptional());
223 factory.addProperty("xml[@optional]", "false");
224 assertTrue("Wrong value of config-optional attribute", decl.isOptional());
225 factory.clearProperty("xml[@config-optional]");
226 factory.setProperty("xml[@optional]", Boolean.TRUE);
227 assertTrue("Old optional attribute not detected", decl.isOptional());
228 }
229
230 /**
231 * Tests whether an invalid value of an optional attribute is detected.
232 */
233 @Test(expected = ConfigurationRuntimeException.class)
234 public void testConfigurationDeclarationOptionalAttributeInvalid()
235 {
236 factory.addProperty("xml.fileName", "test.xml");
237 DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
238 factory, factory.configurationAt("xml"));
239 factory.setProperty("xml[@optional]", "invalid value");
240 decl.isOptional();
241 }
242
243 /**
244 * Tests adding a new configuration provider.
245 */
246 @Test
247 public void testAddConfigurationProvider()
248 {
249 DefaultConfigurationBuilder.ConfigurationProvider provider = new DefaultConfigurationBuilder.ConfigurationProvider();
250 assertNull("Provider already registered", factory
251 .providerForTag("test"));
252 factory.addConfigurationProvider("test", provider);
253 assertSame("Provider not registered", provider, factory
254 .providerForTag("test"));
255 }
256
257 /**
258 * Tries to register a null configuration provider. This should cause an
259 * exception.
260 */
261 @Test(expected = IllegalArgumentException.class)
262 public void testAddConfigurationProviderNull()
263 {
264 factory.addConfigurationProvider("test", null);
265 }
266
267 /**
268 * Tries to register a configuration provider for a null tag. This should
269 * cause an exception to be thrown.
270 */
271 @Test(expected = IllegalArgumentException.class)
272 public void testAddConfigurationProviderNullTag()
273 {
274 factory.addConfigurationProvider(null,
275 new DefaultConfigurationBuilder.ConfigurationProvider());
276 }
277
278 /**
279 * Tests removing configuration providers.
280 */
281 @Test
282 public void testRemoveConfigurationProvider()
283 {
284 assertNull("Removing unknown provider", factory
285 .removeConfigurationProvider("test"));
286 assertNull("Removing provider for null tag", factory
287 .removeConfigurationProvider(null));
288 DefaultConfigurationBuilder.ConfigurationProvider provider = new DefaultConfigurationBuilder.ConfigurationProvider();
289 factory.addConfigurationProvider("test", provider);
290 assertSame("Failed to remove provider", provider, factory
291 .removeConfigurationProvider("test"));
292 assertNull("Provider still registered", factory.providerForTag("test"));
293 }
294
295 /**
296 * Tests creating a configuration object from a configuration declaration.
297 */
298 @Test
299 public void testConfigurationBeanFactoryCreateBean()
300 {
301 factory.addConfigurationProvider("test",
302 new DefaultConfigurationBuilder.ConfigurationProvider(
303 PropertiesConfiguration.class));
304 factory.addProperty("test[@throwExceptionOnMissing]", "true");
305 DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
306 factory, factory.configurationAt("test"));
307 PropertiesConfiguration conf = (PropertiesConfiguration) BeanHelper
308 .createBean(decl);
309 assertTrue("Property was not initialized", conf
310 .isThrowExceptionOnMissing());
311 }
312
313 /**
314 * Tests creating a configuration object from an unknown tag. This should
315 * cause an exception.
316 */
317 @Test(expected = ConfigurationRuntimeException.class)
318 public void testConfigurationBeanFactoryCreateUnknownTag()
319 {
320 factory.addProperty("test[@throwExceptionOnMissing]", "true");
321 DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
322 factory, factory.configurationAt("test"));
323 BeanHelper.createBean(decl);
324 }
325
326 /**
327 * Tests loading a simple configuration definition file.
328 */
329 @Test
330 public void testLoadConfiguration() throws ConfigurationException
331 {
332 factory.setFile(TEST_FILE);
333 checkConfiguration();
334 }
335
336 /**
337 * Tests the file constructor.
338 */
339 @Test
340 public void testLoadConfigurationFromFile() throws ConfigurationException
341 {
342 factory = new DefaultConfigurationBuilder(TEST_FILE);
343 checkConfiguration();
344 }
345
346 /**
347 * Tests the file name constructor.
348 */
349 @Test
350 public void testLoadConfigurationFromFileName()
351 throws ConfigurationException
352 {
353 factory = new DefaultConfigurationBuilder(TEST_FILE.getAbsolutePath());
354 checkConfiguration();
355 }
356
357 /**
358 * Tests the URL constructor.
359 */
360 @Test
361 public void testLoadConfigurationFromURL() throws Exception
362 {
363 factory = new DefaultConfigurationBuilder(TEST_FILE.toURI().toURL());
364 checkConfiguration();
365 }
366
367 /**
368 * Tests if the configuration was correctly created by the factory.
369 */
370 private void checkConfiguration() throws ConfigurationException
371 {
372 CombinedConfiguration compositeConfiguration = (CombinedConfiguration) factory
373 .getConfiguration();
374
375 assertEquals("Number of configurations", 3, compositeConfiguration
376 .getNumberOfConfigurations());
377 assertEquals(PropertiesConfiguration.class, compositeConfiguration
378 .getConfiguration(0).getClass());
379 assertEquals(XMLPropertiesConfiguration.class, compositeConfiguration
380 .getConfiguration(1).getClass());
381 assertEquals(XMLConfiguration.class, compositeConfiguration
382 .getConfiguration(2).getClass());
383
384 // check the first configuration
385 PropertiesConfiguration pc = (PropertiesConfiguration) compositeConfiguration
386 .getConfiguration(0);
387 assertNotNull("Make sure we have a fileName: " + pc.getFileName(), pc
388 .getFileName());
389
390 // check some properties
391 checkProperties(compositeConfiguration);
392 }
393
394 /**
395 * Checks if the passed in configuration contains the expected properties.
396 *
397 * @param compositeConfiguration the configuration to check
398 */
399 private void checkProperties(Configuration compositeConfiguration)
400 {
401 assertTrue("Make sure we have loaded our key", compositeConfiguration
402 .getBoolean("test.boolean"));
403 assertEquals("I'm complex!", compositeConfiguration
404 .getProperty("element2.subelement.subsubelement"));
405 assertEquals("property in the XMLPropertiesConfiguration", "value1",
406 compositeConfiguration.getProperty("key1"));
407 }
408
409 /**
410 * Tests loading a configuration definition file with an additional section.
411 */
412 @Test
413 public void testLoadAdditional() throws ConfigurationException
414 {
415 factory.setFile(ADDITIONAL_FILE);
416 CombinedConfiguration compositeConfiguration = (CombinedConfiguration) factory
417 .getConfiguration();
418 assertEquals("Verify how many configs", 2, compositeConfiguration
419 .getNumberOfConfigurations());
420
421 // Test if union was constructed correctly
422 Object prop = compositeConfiguration.getProperty("tables.table.name");
423 assertTrue(prop instanceof Collection);
424 assertEquals(3, ((Collection<?>) prop).size());
425 assertEquals("users", compositeConfiguration
426 .getProperty("tables.table(0).name"));
427 assertEquals("documents", compositeConfiguration
428 .getProperty("tables.table(1).name"));
429 assertEquals("tasks", compositeConfiguration
430 .getProperty("tables.table(2).name"));
431
432 prop = compositeConfiguration
433 .getProperty("tables.table.fields.field.name");
434 assertTrue(prop instanceof Collection);
435 assertEquals(17, ((Collection<?>) prop).size());
436
437 assertEquals("smtp.mydomain.org", compositeConfiguration
438 .getString("mail.host.smtp"));
439 assertEquals("pop3.mydomain.org", compositeConfiguration
440 .getString("mail.host.pop"));
441
442 // This was overriden
443 assertEquals("masterOfPost", compositeConfiguration
444 .getString("mail.account.user"));
445 assertEquals("topsecret", compositeConfiguration
446 .getString("mail.account.psswd"));
447
448 // This was overriden, too, but not in additional section
449 assertEquals("enhanced factory", compositeConfiguration
450 .getString("test.configuration"));
451 }
452
453 /**
454 * Tests whether a default log error listener is registered at the builder
455 * instance.
456 */
457 @Test
458 public void testLogErrorListener()
459 {
460 assertEquals("No default error listener registered", 1,
461 new DefaultConfigurationBuilder().getErrorListeners().size());
462 }
463
464 /**
465 * Tests loading a definition file that contains optional configurations.
466 */
467 @Test
468 public void testLoadOptional() throws Exception
469 {
470 factory.setURL(OPTIONAL_FILE.toURI().toURL());
471 Configuration config = factory.getConfiguration();
472 assertTrue(config.getBoolean("test.boolean"));
473 assertEquals("value", config.getProperty("element"));
474 }
475
476 /**
477 * Tests whether loading a failing optional configuration causes an error
478 * event.
479 */
480 @Test
481 public void testLoadOptionalErrorEvent() throws Exception
482 {
483 factory.clearErrorListeners();
484 ConfigurationErrorListenerImpl listener = new ConfigurationErrorListenerImpl();
485 factory.addErrorListener(listener);
486 prepareOptionalTest("configuration", false);
487 listener.verify(DefaultConfigurationBuilder.EVENT_ERR_LOAD_OPTIONAL,
488 OPTIONAL_NAME, null);
489 }
490
491 /**
492 * Tests loading a definition file with optional and non optional
493 * configuration sources. One non optional does not exist, so this should
494 * cause an exception.
495 */
496 @Test(expected = ConfigurationException.class)
497 public void testLoadOptionalWithException() throws ConfigurationException
498 {
499 factory.setFile(OPTIONALEX_FILE);
500 factory.getConfiguration();
501 }
502
503 /**
504 * Tries to load a configuration file with an optional, non file-based
505 * configuration. The optional attribute should work for other configuration
506 * classes, too.
507 */
508 @Test
509 public void testLoadOptionalNonFileBased() throws ConfigurationException
510 {
511 CombinedConfiguration config = prepareOptionalTest("configuration", false);
512 assertTrue("Configuration not empty", config.isEmpty());
513 assertEquals("Wrong number of configurations", 0, config
514 .getNumberOfConfigurations());
515 }
516
517 /**
518 * Tests an optional, non existing configuration with the forceCreate
519 * attribute. This configuration should be added to the resulting
520 * configuration.
521 */
522 @Test
523 public void testLoadOptionalForceCreate() throws ConfigurationException
524 {
525 factory.setBasePath(TEST_FILE.getParent());
526 CombinedConfiguration config = prepareOptionalTest("xml", true);
527 assertEquals("Wrong number of configurations", 1, config
528 .getNumberOfConfigurations());
529 FileConfiguration fc = (FileConfiguration) config
530 .getConfiguration(OPTIONAL_NAME);
531 assertNotNull("Optional config not found", fc);
532 assertEquals("File name was not set", "nonExisting.xml", fc
533 .getFileName());
534 assertNotNull("Base path was not set", fc.getBasePath());
535 }
536
537 /**
538 * Tests loading an embedded optional configuration builder with the force
539 * create attribute.
540 */
541 @Test
542 public void testLoadOptionalBuilderForceCreate()
543 throws ConfigurationException
544 {
545 CombinedConfiguration config = prepareOptionalTest("configuration",
546 true);
547 assertEquals("Wrong number of configurations", 1, config
548 .getNumberOfConfigurations());
549 assertTrue(
550 "Wrong optional configuration type",
551 config.getConfiguration(OPTIONAL_NAME) instanceof CombinedConfiguration);
552 }
553
554 /**
555 * Tests loading an optional configuration with the force create attribute
556 * set. The provider will always throw an exception. In this case the
557 * configuration will not be added to the resulting combined configuration.
558 */
559 @Test
560 public void testLoadOptionalForceCreateWithException()
561 throws ConfigurationException
562 {
563 factory.addConfigurationProvider("test",
564 new DefaultConfigurationBuilder.ConfigurationBuilderProvider()
565 {
566 // Throw an exception here, too
567 @Override
568 public AbstractConfiguration getEmptyConfiguration(
569 DefaultConfigurationBuilder.ConfigurationDeclaration decl) throws Exception
570 {
571 throw new Exception("Unable to create configuration!");
572 }
573 });
574 CombinedConfiguration config = prepareOptionalTest("test", true);
575 assertEquals("Optional configuration could be created", 0, config
576 .getNumberOfConfigurations());
577 }
578
579 /**
580 * Prepares a test for loading a configuration definition file with an
581 * optional configuration declaration.
582 *
583 * @param tag the tag name with the optional configuration
584 * @param force the forceCreate attribute
585 * @return the combined configuration obtained from the builder
586 * @throws ConfigurationException if an error occurs
587 */
588 private CombinedConfiguration prepareOptionalTest(String tag, boolean force)
589 throws ConfigurationException
590 {
591 String prefix = "override." + tag;
592 factory.addProperty(prefix + "[@fileName]", "nonExisting.xml");
593 factory.addProperty(prefix + "[@config-optional]", Boolean.TRUE);
594 factory.addProperty(prefix + "[@config-name]", OPTIONAL_NAME);
595 if (force)
596 {
597 factory.addProperty(prefix + "[@config-forceCreate]", Boolean.TRUE);
598 }
599 return factory.getConfiguration(false);
600 }
601
602 /**
603 * Tests whether the error log message caused by an optional configuration
604 * can be suppressed if a child builder is involved.
605 */
606 @Test
607 public void testLoadOptionalChildBuilderSuppressErrorLog()
608 throws ConfigurationException
609 {
610 factory.addProperty("override.configuration[@fileName]",
611 OPTIONAL_FILE.getAbsolutePath());
612 // a special invocation handler which checks that the warn() method of
613 // a logger is not called
614 InvocationHandler handler = new InvocationHandler()
615 {
616 public Object invoke(Object proxy, Method method, Object[] args)
617 throws Throwable
618 {
619 String methodName = method.getName();
620 if (methodName.startsWith("is"))
621 {
622 return Boolean.TRUE;
623 }
624 if ("warn".equals(methodName))
625 {
626 fail("Unexpected log output!");
627 }
628 return null;
629 }
630 };
631 factory.setLogger((Log) Proxy.newProxyInstance(getClass()
632 .getClassLoader(), new Class[] {
633 Log.class
634 }, handler));
635 factory.getConfiguration(false);
636 }
637
638 /**
639 * Tests loading a definition file with multiple different sources.
640 */
641 @Test
642 public void testLoadDifferentSources() throws ConfigurationException
643 {
644 factory.setFile(MULTI_FILE);
645 Configuration config = factory.getConfiguration();
646 assertFalse(config.isEmpty());
647 assertTrue(config instanceof CombinedConfiguration);
648 CombinedConfiguration cc = (CombinedConfiguration) config;
649 assertEquals("Wrong number of configurations", 1, cc
650 .getNumberOfConfigurations());
651
652 assertNotNull(config
653 .getProperty("tables.table(0).fields.field(2).name"));
654 assertNotNull(config.getProperty("element2.subelement.subsubelement"));
655 assertEquals("value", config.getProperty("element3"));
656 assertEquals("foo", config.getProperty("element3[@name]"));
657 assertNotNull(config.getProperty("mail.account.user"));
658
659 // test JNDIConfiguration
660 assertNotNull(config.getProperty("test.onlyinjndi"));
661 assertTrue(config.getBoolean("test.onlyinjndi"));
662
663 Configuration subset = config.subset("test");
664 assertNotNull(subset.getProperty("onlyinjndi"));
665 assertTrue(subset.getBoolean("onlyinjndi"));
666
667 // test SystemConfiguration
668 assertNotNull(config.getProperty("java.version"));
669 assertEquals(System.getProperty("java.version"), config
670 .getString("java.version"));
671
672 // test INIConfiguration
673 assertEquals("Property from ini file not found", "yes",
674 config.getString("testini.loaded"));
675
676 // test environment configuration
677 EnvironmentConfiguration envConf = new EnvironmentConfiguration();
678 for (Iterator<String> it = envConf.getKeys(); it.hasNext();)
679 {
680 String key = it.next();
681 String combinedKey = "env." + key;
682 assertEquals("Wrong value for env property " + key,
683 envConf.getString(key), config.getString(combinedKey));
684 }
685 }
686
687 /**
688 * Tests if the base path is correctly evaluated.
689 */
690 @Test
691 public void testSetConfigurationBasePath() throws ConfigurationException
692 {
693 factory.addProperty("properties[@fileName]", "test.properties");
694 File deepDir = new File(ConfigurationAssert.TEST_DIR, "config/deep");
695 factory.setConfigurationBasePath(deepDir.getAbsolutePath());
696
697 Configuration config = factory.getConfiguration(false);
698 assertEquals("Wrong property value", "somevalue", config
699 .getString("somekey"));
700 }
701
702 /**
703 * Tests reading a configuration definition file that contains complex
704 * initialization of properties of the declared configuration sources.
705 */
706 @Test
707 public void testComplexInitialization() throws ConfigurationException
708 {
709 factory.setFile(INIT_FILE);
710 CombinedConfiguration cc = (CombinedConfiguration) factory
711 .getConfiguration();
712
713 assertEquals("System property not found", "test.xml",
714 cc.getString("test_file_xml"));
715 PropertiesConfiguration c1 = (PropertiesConfiguration) cc
716 .getConfiguration(1);
717 assertTrue(
718 "Reloading strategy was not set",
719 c1.getReloadingStrategy() instanceof FileChangedReloadingStrategy);
720 assertEquals("Refresh delay was not set", 10000,
721 ((FileChangedReloadingStrategy) c1.getReloadingStrategy())
722 .getRefreshDelay());
723
724 Configuration xmlConf = cc.getConfiguration("xml");
725 assertEquals("Property not found", "I'm complex!", xmlConf
726 .getString("element2/subelement/subsubelement"));
727 assertEquals("List index not found", "two", xmlConf
728 .getString("list[0]/item[1]"));
729 assertEquals("Property in combiner file not found", "yellow", cc
730 .getString("/gui/selcolor"));
731
732 assertTrue("Delimiter flag was not set", cc
733 .isDelimiterParsingDisabled());
734 assertTrue("Expression engine was not set",
735 cc.getExpressionEngine() instanceof XPathExpressionEngine);
736 }
737
738 /**
739 * Tests if the returned combined configuration has the expected structure.
740 */
741 @Test
742 public void testCombinedConfigurationStructure() throws ConfigurationException
743 {
744 factory.setFile(INIT_FILE);
745 CombinedConfiguration cc = (CombinedConfiguration) factory
746 .getConfiguration();
747 assertNotNull("Properties configuration not found", cc
748 .getConfiguration("properties"));
749 assertNotNull("XML configuration not found", cc.getConfiguration("xml"));
750 assertEquals("Wrong number of contained configs", 4, cc
751 .getNumberOfConfigurations());
752
753 CombinedConfiguration cc2 = (CombinedConfiguration) cc
754 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME);
755 assertNotNull("No additional configuration found", cc2);
756 Set<String> names = cc2.getConfigurationNames();
757 assertEquals("Wrong number of contained additional configs", 2, names
758 .size());
759 assertTrue("Config 1 not contained", names.contains("combiner1"));
760 assertTrue("Config 2 not contained", names.contains("combiner2"));
761 }
762
763 /**
764 * Helper method for testing the attributes of a combined configuration
765 * created by the builder.
766 *
767 * @param cc the configuration to be checked
768 */
769 private void checkCombinedConfigAttrs(CombinedConfiguration cc)
770 {
771 assertTrue("Wrong delimiter parsing flag",
772 cc.isDelimiterParsingDisabled());
773 assertTrue("Wrong reload check", cc.isForceReloadCheck());
774 assertTrue("Wrong ignore reload ex flag", cc.isIgnoreReloadExceptions());
775 }
776
777 /**
778 * Tests whether attributes are correctly set on the combined configurations
779 * for the override and additional sections.
780 */
781 @Test
782 public void testCombinedConfigurationAttributes() throws ConfigurationException
783 {
784 factory.setFile(INIT_FILE);
785 CombinedConfiguration cc = (CombinedConfiguration) factory
786 .getConfiguration();
787 checkCombinedConfigAttrs(cc);
788 CombinedConfiguration cc2 = (CombinedConfiguration) cc
789 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME);
790 checkCombinedConfigAttrs(cc2);
791 }
792
793 /**
794 * Tests the structure of the returned combined configuration if there is no
795 * additional section.
796 */
797 @Test
798 public void testCombinedConfigurationNoAdditional()
799 throws ConfigurationException
800 {
801 factory.setFile(TEST_FILE);
802 CombinedConfiguration cc = factory.getConfiguration(true);
803 assertNull("Additional configuration was found", cc
804 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME));
805 }
806
807 /**
808 * Tests whether the list node definition was correctly processed.
809 */
810 @Test
811 public void testCombinedConfigurationListNodes()
812 throws ConfigurationException
813 {
814 factory.setFile(INIT_FILE);
815 CombinedConfiguration cc = factory.getConfiguration(true);
816 Set<String> listNodes = cc.getNodeCombiner().getListNodes();
817 assertEquals("Wrong number of list nodes", 2, listNodes.size());
818 assertTrue("table node not a list node", listNodes.contains("table"));
819 assertTrue("list node not a list node", listNodes.contains("list"));
820
821 CombinedConfiguration cca = (CombinedConfiguration) cc
822 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME);
823 listNodes = cca.getNodeCombiner().getListNodes();
824 assertTrue("Found list nodes for additional combiner", listNodes
825 .isEmpty());
826 }
827
828 /**
829 * Tests whether a configuration builder can itself be declared in a
830 * configuration definition file.
831 */
832 @Test
833 public void testConfigurationBuilderProvider()
834 throws ConfigurationException
835 {
836 factory.addProperty("override.configuration[@fileName]", TEST_FILE
837 .getAbsolutePath());
838 CombinedConfiguration cc = factory.getConfiguration(false);
839 assertEquals("Wrong number of configurations", 1, cc
840 .getNumberOfConfigurations());
841 checkProperties(cc);
842 }
843
844 /**
845 * Tests whether settings of the builder are propagated to child builders.
846 */
847 @Test
848 public void testConfigurationBuilderProviderInheritProperties()
849 throws Exception
850 {
851 factory.addProperty("override.configuration[@fileName]",
852 TEST_FILE.getAbsolutePath());
853 factory.setBasePath("conf");
854 factory.setAttributeSplittingDisabled(true);
855 factory.setDelimiterParsingDisabled(true);
856 factory.setListDelimiter('/');
857 factory.setThrowExceptionOnMissing(true);
858 Log log = LogFactory.getLog(getClass());
859 factory.setLogger(log);
860 factory.clearErrorListeners();
861 factory.clearConfigurationListeners();
862 ConfigurationListenerTestImpl l =
863 new ConfigurationListenerTestImpl(factory);
864 factory.addConfigurationListener(l);
865 DefaultConfigurationBuilder.ConfigurationDeclaration decl =
866 new DefaultConfigurationBuilder.ConfigurationDeclaration(
867 factory,
868 factory.configurationAt("override.configuration"));
869 DefaultConfigurationBuilder.ConfigurationBuilderProvider provider =
870 new DefaultConfigurationBuilder.ConfigurationBuilderProvider();
871 DefaultConfigurationBuilder child =
872 (DefaultConfigurationBuilder) provider.createBean(
873 provider.fetchConfigurationClass(), decl, null);
874 assertEquals("Wrong base path", factory.getBasePath(),
875 child.getBasePath());
876 assertEquals("Wrong attribute splitting flag",
877 factory.isAttributeSplittingDisabled(),
878 child.isAttributeSplittingDisabled());
879 assertEquals("Wrong delimiter parsing flag",
880 factory.isDelimiterParsingDisabled(),
881 child.isDelimiterParsingDisabled());
882 assertEquals("Wrong list delimiter", factory.getListDelimiter(),
883 child.getListDelimiter());
884 assertEquals("Wrong exception flag",
885 factory.isThrowExceptionOnMissing(),
886 child.isThrowExceptionOnMissing());
887 assertSame("Wrong logger", log, child.getLogger());
888 assertTrue("Got error listeners", child.getErrorListeners().isEmpty());
889 assertEquals("Wrong number of listeners", 1, child
890 .getConfigurationListeners().size());
891 assertEquals("Wrong listener", l, child.getConfigurationListeners()
892 .iterator().next());
893 }
894
895 /**
896 * Tests whether properties of the parent configuration can be overridden.
897 */
898 @Test
899 public void testConfigurationBuilderProviderOverrideProperties()
900 throws Exception
901 {
902 factory.addProperty("override.configuration[@fileName]",
903 TEST_FILE.getAbsolutePath());
904 factory.addProperty("override.configuration[@basePath]", "base");
905 factory.addProperty("override.configuration[@throwExceptionOnMissing]",
906 "false");
907 factory.setBasePath("conf");
908 factory.setThrowExceptionOnMissing(true);
909 DefaultConfigurationBuilder.ConfigurationDeclaration decl =
910 new DefaultConfigurationBuilder.ConfigurationDeclaration(
911 factory,
912 factory.configurationAt("override.configuration"));
913 DefaultConfigurationBuilder.ConfigurationBuilderProvider provider =
914 new DefaultConfigurationBuilder.ConfigurationBuilderProvider();
915 DefaultConfigurationBuilder child =
916 (DefaultConfigurationBuilder) provider.createBean(
917 provider.fetchConfigurationClass(), decl, null);
918 assertEquals("Wrong base path", "base", child.getBasePath());
919 assertFalse("Wrong exception flag", child.isThrowExceptionOnMissing());
920 }
921
922 /**
923 * Tests whether XML settings can be inherited.
924 */
925 @Test
926 public void testLoadXMLWithSettings() throws Exception
927 {
928 File confDir = new File("conf");
929 File targetDir = new File("target");
930 File testXMLValidationSource = new File(confDir,
931 "testValidateInvalid.xml");
932 File testSavedXML = new File(targetDir, "testSave.xml");
933 File testSavedFactory = new File(targetDir, "testSaveFactory.xml");
934 URL dtdFile = getClass().getResource("/properties.dtd");
935 final String publicId = "http://commons.apache.org/test.dtd";
936
937 XMLConfiguration config = new XMLConfiguration("testDtd.xml");
938 config.setPublicID(publicId);
939 config.save(testSavedXML);
940 factory.addProperty("xml[@fileName]", testSavedXML.getAbsolutePath());
941 factory.addProperty("xml(0)[@validating]", "true");
942 factory.addProperty("xml(-1)[@fileName]", testXMLValidationSource
943 .getAbsolutePath());
944 factory.addProperty("xml(1)[@config-optional]", "true");
945 factory.addProperty("xml(1)[@validating]", "true");
946 factory.save(testSavedFactory);
947
948 factory = new DefaultConfigurationBuilder();
949 factory.setFile(testSavedFactory);
950 factory.registerEntityId(publicId, dtdFile);
951 factory.clearErrorListeners();
952 Configuration c = factory.getConfiguration();
953 assertEquals("Wrong property value", "value1", c.getString("entry(0)"));
954 assertFalse("Invalid XML source was loaded", c
955 .containsKey("table.name"));
956
957 testSavedXML.delete();
958 testSavedFactory.delete();
959 }
960
961 /**
962 * Tests loading a configuration definition file that defines a custom
963 * result class.
964 */
965 @Test
966 public void testExtendedClass() throws ConfigurationException
967 {
968 factory.setFile(CLASS_FILE);
969 CombinedConfiguration cc = factory.getConfiguration(true);
970 assertEquals("Extended", cc.getProperty("test"));
971 assertTrue("Wrong result class: " + cc.getClass(),
972 cc instanceof ExtendedCombinedConfiguration);
973 }
974
975 /**
976 * Tests loading a configuration definition file that defines new providers.
977 */
978 @Test
979 public void testConfigurationProvider() throws ConfigurationException
980 {
981 factory.setFile(PROVIDER_FILE);
982 factory.getConfiguration(true);
983 DefaultConfigurationBuilder.ConfigurationProvider provider = factory
984 .providerForTag("test");
985 assertNotNull("Provider 'test' not registered", provider);
986 }
987
988 /**
989 * Tests loading a configuration definition file that defines new providers.
990 */
991 @Test
992 public void testExtendedXMLConfigurationProvider() throws ConfigurationException
993 {
994 factory.setFile(EXTENDED_PROVIDER_FILE);
995 CombinedConfiguration cc = factory.getConfiguration(true);
996 DefaultConfigurationBuilder.ConfigurationProvider provider = factory
997 .providerForTag("test");
998 assertNotNull("Provider 'test' not registered", provider);
999 Configuration config = cc.getConfiguration("xml");
1000 assertNotNull("Test configuration not present", config);
1001 assertTrue("Configuration is not ExtendedXMLConfiguration, is " +
1002 config.getClass().getName(), config instanceof ExtendedXMLConfiguration);
1003 }
1004
1005 @Test
1006 public void testGlobalLookup() throws Exception
1007 {
1008 factory.setFile(GLOBAL_LOOKUP_FILE);
1009 CombinedConfiguration cc = factory.getConfiguration(true);
1010 String value = cc.getInterpolator().lookup("test:test_key");
1011 assertNotNull("The test key was not located", value);
1012 assertEquals("Incorrect value retrieved","test.value",value);
1013 }
1014
1015 @Test
1016 public void testSystemProperties() throws Exception
1017 {
1018 factory.setFile(SYSTEM_PROPS_FILE);
1019 factory.getConfiguration(true);
1020 String value = System.getProperty("key1");
1021 assertNotNull("The test key was not located", value);
1022 assertEquals("Incorrect value retrieved","value1",value);
1023 }
1024
1025 @Test
1026 public void testValidation() throws Exception
1027 {
1028 factory.setFile(VALIDATION_FILE);
1029 factory.getConfiguration(true);
1030 String value = System.getProperty("key1");
1031 assertNotNull("The test key was not located", value);
1032 assertEquals("Incorrect value retrieved","value1",value);
1033 }
1034
1035 @Test
1036 public void testValidation3() throws Exception
1037 {
1038 System.getProperties().remove("Id");
1039 factory.setFile(VALIDATION3_FILE);
1040 CombinedConfiguration config = factory.getConfiguration(true);
1041 String value = config.getString("Employee/Name");
1042 assertNotNull("The test key was not located", value);
1043 assertEquals("Incorrect value retrieved","John Doe",value);
1044 System.setProperty("Id", "1001");
1045 value = config.getString("Employee/Name");
1046 assertNotNull("The test key was not located", value);
1047 assertEquals("Incorrect value retrieved","Jane Doe",value);
1048 }
1049
1050 @Test
1051 public void testMultiTenentConfiguration() throws Exception
1052 {
1053 factory.setFile(MULTI_TENENT_FILE);
1054 System.getProperties().remove("Id");
1055
1056 CombinedConfiguration config = factory.getConfiguration(true);
1057 assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
1058
1059 verify("1001", config, 15);
1060 verify("1002", config, 25);
1061 verify("1003", config, 35);
1062 verify("1004", config, 50);
1063 verify("1005", config, 50);
1064 }
1065
1066 @Test
1067 public void testMultiTenentConfiguration2() throws Exception
1068 {
1069 factory.setFile(MULTI_TENENT_FILE);
1070 System.setProperty("Id", "1004");
1071
1072 CombinedConfiguration config = factory.getConfiguration(true);
1073 assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
1074
1075 verify("1001", config, 15);
1076 verify("1002", config, 25);
1077 verify("1003", config, 35);
1078 verify("1004", config, 50);
1079 verify("1005", config, 50);
1080 }
1081
1082 @Test
1083 public void testMultiTenentConfiguration3() throws Exception
1084 {
1085 factory.setFile(MULTI_TENENT_FILE);
1086 StringWriter writer = new StringWriter();
1087 WriterAppender app = new WriterAppender(new SimpleLayout(), writer);
1088 Log log = LogFactory.getLog("TestLogger");
1089 Logger logger = ((Log4JLogger)log).getLogger();
1090 logger.addAppender(app);
1091 logger.setLevel(Level.DEBUG);
1092 logger.setAdditivity(false);
1093
1094 System.setProperty("Id", "1005");
1095
1096 CombinedConfiguration config = factory.getConfiguration(true);
1097 assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
1098
1099 verify("1001", config, 15);
1100 String xml = writer.getBuffer().toString();
1101 assertNotNull("No XML returned", xml);
1102 assertTrue("Incorect configuration data", xml.indexOf("<rowsPerPage>15</rowsPerPage>") >= 0);
1103 logger.removeAppender(app);
1104 logger.setLevel(Level.OFF);
1105 verify("1002", config, 25);
1106 verify("1003", config, 35);
1107 verify("1004", config, 50);
1108 verify("1005", config, 50);
1109 }
1110
1111 @Test
1112 public void testMultiTenantConfigurationAt() throws Exception
1113 {
1114 factory.setFile(MULTI_TENENT_FILE);
1115 System.setProperty("Id", "1001");
1116 CombinedConfiguration config = factory.getConfiguration(true);
1117 HierarchicalConfiguration sub1 = config.configurationAt("Channels/Channel[@id='1']");
1118 assertEquals("My Channel", sub1.getString("Name"));
1119 assertEquals("test 1 data", sub1.getString("ChannelData"));
1120 HierarchicalConfiguration sub2 = config.configurationAt("Channels/Channel[@id='2']");
1121 assertEquals("Channel 2", sub2.getString("Name"));
1122 assertEquals("more test 2 data", sub2.getString("MoreChannelData"));
1123 }
1124
1125 @Test
1126 public void testMerge() throws Exception
1127 {
1128 factory.setFile(MULTI_TENENT_FILE);
1129 System.setProperty("Id", "1004");
1130 Map<String, String> map = new HashMap<String, String>();
1131 map.put("default", "${colors.header4}");
1132 map.put("background", "#40404040");
1133 map.put("text", "#000000");
1134 map.put("header", "#444444");
1135
1136 CombinedConfiguration config = factory.getConfiguration(true);
1137 assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
1138
1139 List<HierarchicalConfiguration> list = config.configurationsAt("colors/*");
1140 Iterator<HierarchicalConfiguration> iter = list.iterator();
1141 while (iter.hasNext())
1142 {
1143 SubnodeConfiguration sub = (SubnodeConfiguration)iter.next();
1144 ConfigurationNode node = sub.getRootNode();
1145 String value = (node.getValue() == null) ? "null" : node.getValue().toString();
1146 if (map.containsKey(node.getName()))
1147 {
1148 assertEquals(map.get(node.getName()), value);
1149 }
1150 }
1151
1152 }
1153
1154 @Test
1155 public void testDelimiterParsingDisabled() throws Exception
1156 {
1157 factory.setFile(MULTI_TENENT_FILE);
1158 System.setProperty("Id", "1004");
1159
1160 CombinedConfiguration config = factory.getConfiguration(true);
1161 assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
1162
1163 assertEquals("a,b,c", config.getString("split/list3/@values"));
1164 assertEquals(0, config.getMaxIndex("split/list3/@values"));
1165 assertEquals("a\\,b\\,c", config.getString("split/list4/@values"));
1166 assertEquals("a,b,c", config.getString("split/list1"));
1167 assertEquals(0, config.getMaxIndex("split/list1"));
1168 assertEquals("a\\,b\\,c", config.getString("split/list2"));
1169 }
1170
1171 @Test
1172 public void testExpression() throws Exception
1173 {
1174 if (SystemUtils.isJavaVersionAtLeast(150))
1175 {
1176 factory.setFile(EXPRESSION_FILE);
1177 factory.setAttributeSplittingDisabled(true);
1178 System.getProperties().remove("Id");
1179 org.slf4j.MDC.clear();
1180
1181 CombinedConfiguration config = factory.getConfiguration(true);
1182 assertTrue("Incorrect configuration",
1183 config instanceof DynamicCombinedConfiguration);
1184
1185 verify("1001", config, 15);
1186 }
1187 }
1188
1189 /**
1190 * Tests whether variable substitution works across multiple child
1191 * configurations. This test is related to CONFIGURATION-481.
1192 */
1193 @Test
1194 public void testInterpolationOverMultipleSources()
1195 throws ConfigurationException
1196 {
1197 File testFile =
1198 ConfigurationAssert.getTestFile("testInterpolationBuilder.xml");
1199 factory.setFile(testFile);
1200 CombinedConfiguration combConfig = factory.getConfiguration(true);
1201 assertEquals("Wrong value", "abc-product",
1202 combConfig.getString("products.product.desc"));
1203 XMLConfiguration xmlConfig =
1204 (XMLConfiguration) combConfig.getConfiguration("test");
1205 assertEquals("Wrong value from XML config", "abc-product",
1206 xmlConfig.getString("products/product/desc"));
1207 SubnodeConfiguration subConfig =
1208 xmlConfig
1209 .configurationAt("products/product[@name='abc']", true);
1210 assertEquals("Wrong value from sub config", "abc-product",
1211 subConfig.getString("desc"));
1212 }
1213
1214 private void verify(String key, CombinedConfiguration config, int rows)
1215 {
1216 System.setProperty("Id", key);
1217 org.slf4j.MDC.put("Id", key);
1218 int actual = config.getInt("rowsPerPage");
1219 assertTrue("expected: " + rows + " actual: " + actual, actual == rows);
1220 }
1221
1222
1223 /**
1224 * A specialized combined configuration implementation used for testing
1225 * custom result classes.
1226 */
1227 public static class ExtendedCombinedConfiguration extends
1228 CombinedConfiguration
1229 {
1230 /**
1231 * The serial version UID.
1232 */
1233 private static final long serialVersionUID = 4678031745085083392L;
1234
1235 @Override
1236 public Object getProperty(String key)
1237 {
1238 if (key.equals("test"))
1239 {
1240 return "Extended";
1241 }
1242 return super.getProperty(key);
1243 }
1244 }
1245
1246 public static class ExtendedXMLConfiguration extends XMLConfiguration
1247 {
1248 private static final long serialVersionUID = 1L;
1249
1250 public ExtendedXMLConfiguration()
1251 {
1252 }
1253
1254 }
1255
1256 public static class TestLookup extends StrLookup
1257 {
1258 Map<String, String> map = new HashMap<String, String>();
1259
1260 public TestLookup()
1261 {
1262 map.put("test_file_xml", "test.xml");
1263 map.put("test_file_combine", "testcombine1.xml");
1264 map.put("test_key", "test.value");
1265 }
1266
1267 @Override
1268 public String lookup(String key)
1269 {
1270 if (key == null)
1271 {
1272 return null;
1273 }
1274 return map.get(key);
1275
1276 }
1277 }
1278 }
1279