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