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    
018    package org.apache.commons.configuration;
019    
020    import static org.junit.Assert.assertEquals;
021    import static org.junit.Assert.assertFalse;
022    import static org.junit.Assert.assertNotNull;
023    import static org.junit.Assert.assertNotSame;
024    import static org.junit.Assert.assertNull;
025    import static org.junit.Assert.assertTrue;
026    
027    import java.io.File;
028    import java.io.FileInputStream;
029    import java.io.FileOutputStream;
030    import java.io.FileWriter;
031    import java.io.IOException;
032    import java.io.PrintWriter;
033    import java.net.URL;
034    import java.util.Iterator;
035    import java.util.Properties;
036    
037    import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
038    import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
039    import org.junit.Before;
040    import org.junit.Rule;
041    import org.junit.Test;
042    import org.junit.rules.TemporaryFolder;
043    
044    /**
045     * @author Emmanuel Bourg
046     * @version $Id: TestFileConfiguration.java 1231721 2012-01-15 18:32:07Z oheger $
047     */
048    public class TestFileConfiguration
049    {
050        /** Constant for the name of a test file.*/
051        private static final String TEST_FILENAME = "test.properties";
052    
053        /** Constant for a test file.*/
054        private static final File TEST_FILE = ConfigurationAssert.getTestFile(TEST_FILENAME);
055    
056        /** Constant for a test output file. */
057        private static final File OUT_FILE = new File(
058                "target/test-resources/foo/bar/test.properties");
059    
060        /** Constant for the name of a resource to be resolved.*/
061        private static final String RESOURCE_NAME = "config/deep/deeptest.properties";
062    
063        /** Helper object for managing temporary files. */
064        @Rule
065        public TemporaryFolder folder = new TemporaryFolder();
066    
067        /**
068         * Initializes the test environment. This implementation ensures that the
069         * test output file does not exist.
070         */
071        @Before
072        public void setUp() throws Exception
073        {
074            removeOutFile();
075        }
076    
077        /**
078         * Performs cleanup after a test case. This implementation removes temporary
079         * files that have been created.
080         */
081        protected void tearDown() throws Exception
082        {
083            removeOutFile();
084        }
085    
086        /**
087         * Removes a file if it exists.
088         *
089         * @param file the file to be removed
090         */
091        private static void removeFile(File file)
092        {
093            if (file.exists())
094            {
095                assertTrue("Cannot remove file: " + file, file.delete());
096            }
097        }
098    
099        /**
100         * Removes the test output file if it exists. Its parent directories are
101         * also removed.
102         */
103        private static void removeOutFile()
104        {
105            removeFile(OUT_FILE);
106            File parent = OUT_FILE.getParentFile();
107            removeFile(parent);
108            parent = parent.getParentFile();
109            removeFile(parent);
110        }
111    
112        @Test
113        public void testSetURL() throws Exception
114        {
115            // http URL
116            FileConfiguration config = new PropertiesConfiguration();
117            config.setURL(new URL("http://commons.apache.org/configuration/index.html"));
118    
119            assertEquals("base path", "http://commons.apache.org/configuration/", config.getBasePath());
120            assertEquals("file name", "index.html", config.getFileName());
121    
122            // file URL - This url is invalid, a valid url would be file:///temp/test.properties.
123            config.setURL(new URL("file:/temp/test.properties"));
124            assertEquals("base path", "file:///temp/", config.getBasePath());
125            assertEquals("file name", "test.properties", config.getFileName());
126        }
127    
128        @Test
129        public void testSetURLWithParams() throws Exception
130        {
131            FileConfiguration config = new PropertiesConfiguration();
132            URL url = new URL("http://issues.apache.org/bugzilla/show_bug.cgi?id=37886");
133            config.setURL(url);
134            assertEquals("Base path incorrect", "http://issues.apache.org/bugzilla/", config.getBasePath());
135            assertEquals("File name incorrect", "show_bug.cgi", config.getFileName());
136            assertEquals("URL was not correctly stored", url, config.getURL());
137        }
138    
139        @Test
140        public void testLocations() throws Exception
141        {
142            PropertiesConfiguration config = new PropertiesConfiguration();
143    
144            File directory = ConfigurationAssert.TEST_DIR;
145            File file = TEST_FILE;
146            config.setFile(file);
147            assertEquals(directory.getAbsolutePath(), config.getBasePath());
148            assertEquals(TEST_FILENAME, config.getFileName());
149            assertEquals(file.getAbsolutePath(), config.getPath());
150    
151            config.setPath(ConfigurationAssert.TEST_DIR_NAME + File.separator + TEST_FILENAME);
152            assertEquals(TEST_FILENAME, config.getFileName());
153            assertEquals(directory.getAbsolutePath(), config.getBasePath());
154            assertEquals(file.getAbsolutePath(), config.getPath());
155            assertEquals(file.toURI().toURL(), config.getURL());
156    
157            config.setBasePath(null);
158            config.setFileName(TEST_FILENAME);
159            assertNull(config.getBasePath());
160            assertEquals(TEST_FILENAME, config.getFileName());
161        }
162    
163        @Test
164        public void testCreateFile1() throws Exception
165        {
166            assertFalse("The file should not exist", OUT_FILE.exists());
167    
168            FileConfiguration config = new PropertiesConfiguration(OUT_FILE);
169            config.save();
170    
171            assertTrue("The file doesn't exist", OUT_FILE.exists());
172        }
173    
174        @Test
175        public void testCreateFile2() throws Exception
176        {
177            FileConfiguration config = new PropertiesConfiguration();
178            config.setFile(OUT_FILE);
179            config.save();
180    
181            assertTrue("The file doesn't exist", OUT_FILE.exists());
182        }
183    
184        @Test
185        public void testCreateFile3() throws Exception
186        {
187            FileConfiguration config = new PropertiesConfiguration();
188            config.save(OUT_FILE);
189    
190            assertTrue("The file doesn't exist", OUT_FILE.exists());
191        }
192    
193        /**
194         * Tests collaboration with ConfigurationFactory: Is the base path set on
195         * loading is valid in file based configurations?
196         *
197         * @throws Exception if an error occurs
198         */
199        @SuppressWarnings("deprecation")
200        @Test
201        public void testWithConfigurationFactory() throws Exception
202        {
203            File file = folder.newFile();
204    
205            ConfigurationFactory factory = new ConfigurationFactory();
206            factory.setConfigurationURL(ConfigurationAssert.getTestURL(
207                    "testDigesterConfiguration2.xml"));
208            CompositeConfiguration cc =
209                    (CompositeConfiguration) factory.getConfiguration();
210            PropertiesConfiguration config = null;
211            for (int i = 0; config == null; i++)
212            {
213                if (cc.getConfiguration(i) instanceof PropertiesConfiguration)
214                {
215                    config = (PropertiesConfiguration) cc.getConfiguration(i);
216                }
217            }
218    
219            config.setProperty("test", "yes");
220            config.save(file);
221            assertTrue(file.exists());
222            config = new PropertiesConfiguration();
223            config.setFile(file);
224            config.load();
225    
226            assertEquals("yes", config.getProperty("test"));
227            assertEquals("masterOfPost", config.getProperty("mail.account.user"));
228        }
229    
230        /**
231         * Tests if invalid URLs cause an exception.
232         */
233        @Test(expected = ConfigurationException.class)
234        public void testSaveInvalidURL() throws Exception
235        {
236            FileConfiguration config = new PropertiesConfiguration();
237            config.save(new URL("http://jakarta.apache.org/test.properties"));
238        }
239    
240        /**
241         * Tests if an invalid URL string causes an exception.
242         */
243        @Test(expected = ConfigurationException.class)
244        public void testSaveInvalidURLString() throws ConfigurationException
245        {
246            FileConfiguration config = new PropertiesConfiguration();
247            config.save("http://www.apache.org/test.properties");
248        }
249    
250        /**
251         * Tests if the URL used by the load() method is also used by save().
252         */
253        @Test
254        public void testFileOverwrite() throws Exception
255        {
256            FileOutputStream out = null;
257            FileInputStream in = null;
258            File tempFile = null;
259            try
260            {
261                tempFile = folder.newFile();
262                Properties props = new Properties();
263                props.setProperty("1", "one");
264                out = new FileOutputStream(tempFile);
265                props.store(out, "TestFileOverwrite");
266                out.close();
267                out = null;
268                FileConfiguration config = new PropertiesConfiguration(tempFile);
269                config.load();
270                String value = config.getString("1");
271                assertTrue("one".equals(value));
272                config.setProperty("1", "two");
273                config.save();
274                props = new Properties();
275                in = new FileInputStream(tempFile);
276                props.load(in);
277                String value2 = props.getProperty("1");
278                assertTrue("two".equals(value2));
279            }
280            finally
281            {
282                if (out != null)
283                {
284                    try
285                    {
286                        out.close();
287                    }
288                    catch (IOException ioex)
289                    {
290                        ioex.printStackTrace();
291                    }
292                }
293                if (in != null)
294                {
295                    try
296                    {
297                        in.close();
298                    }
299                    catch (IOException ioex)
300                    {
301                        ioex.printStackTrace();
302                    }
303                }
304            }
305        }
306    
307        /**
308         * Tests setting a file changed reloading strategy together with the auto
309         * save feature.
310         */
311        @Test
312        public void testReloadingWithAutoSave() throws Exception
313        {
314            File configFile = folder.newFile();
315            PrintWriter out = null;
316    
317            try
318            {
319                out = new PrintWriter(new FileWriter(configFile));
320                out.println("a = one");
321                out.close();
322                out = null;
323    
324                PropertiesConfiguration config = new PropertiesConfiguration(
325                        configFile);
326                config.setReloadingStrategy(new FileChangedReloadingStrategy());
327                config.setAutoSave(true);
328    
329                assertEquals("one", config.getProperty("a"));
330                config.setProperty("b", "two");
331                assertEquals("one", config.getProperty("a"));
332            }
333            finally
334            {
335                if (out != null)
336                {
337                    out.close();
338                }
339            }
340        }
341    
342        /**
343         * Tests loading and saving a configuration file with a complicated path
344         * name including spaces. (related to issue 35210)
345         */
346        @Test
347        public void testPathWithSpaces() throws Exception
348        {
349            File path = folder.newFolder("path with spaces");
350            File confFile = new File(path, "config-test.properties");
351            PrintWriter out = null;
352    
353            try
354            {
355                out = new PrintWriter(new FileWriter(confFile));
356                out.println("saved = false");
357                out.close();
358                out = null;
359    
360                URL url = confFile.toURI().toURL();
361                PropertiesConfiguration config = new PropertiesConfiguration(url);
362                config.load();
363                assertFalse(config.getBoolean("saved"));
364    
365                config.setProperty("saved", Boolean.TRUE);
366                config.save();
367                config = new PropertiesConfiguration();
368                config.setFile(confFile);
369                config.load();
370                assertTrue(config.getBoolean("saved"));
371            }
372            finally
373            {
374                if (out != null)
375                {
376                    out.close();
377                }
378            }
379        }
380    
381        /**
382         * Tests whether file names containing a "+" character are handled
383         * correctly. This test is related to CONFIGURATION-415.
384         */
385        @Test
386        public void testPathWithPlus() throws ConfigurationException, IOException
387        {
388            File saveFile = folder.newFile("test+config.properties");
389            FileConfiguration config = new PropertiesConfiguration(saveFile);
390            config.addProperty("test", Boolean.TRUE);
391            config.save();
392            File configFile = config.getFile();
393            assertEquals("Wrong configuration file", saveFile, configFile);
394        }
395    
396        /**
397         * Tests the getFile() method.
398         */
399        @Test
400        public void testGetFile() throws ConfigurationException
401        {
402            FileConfiguration config = new PropertiesConfiguration();
403            assertNull(config.getFile());
404            File file = TEST_FILE.getAbsoluteFile();
405            config.setFile(file);
406            assertEquals(file, config.getFile());
407            config.load();
408            assertEquals(file, config.getFile());
409        }
410    
411        /**
412         * Tests whether getFile() returns a valid file after a configuration has
413         * been loaded.
414         */
415        @Test
416        public void testGetFileAfterLoad() throws ConfigurationException,
417                IOException
418        {
419            FileConfiguration config = new PropertiesConfiguration();
420            config.load(TEST_FILE.getAbsolutePath());
421            assertNotNull("No source URL set", config.getURL());
422            assertEquals("Wrong source file", TEST_FILE.getCanonicalFile(), config
423                    .getFile().getCanonicalFile());
424        }
425    
426        /**
427         * Tests whether calling load() multiple times changes the source. This
428         * should not be the case.
429         */
430        @Test
431        public void testLoadMultiple() throws ConfigurationException
432        {
433            FileConfiguration config = new PropertiesConfiguration();
434            config.load(TEST_FILE.getAbsolutePath());
435            URL srcUrl = config.getURL();
436            File srcFile = config.getFile();
437            File file2 = ConfigurationAssert.getTestFile("testEqual.properties");
438            config.load(file2.getAbsolutePath());
439            assertEquals("Source URL was changed", srcUrl, config.getURL());
440            assertEquals("Source file was changed", srcFile, config.getFile());
441        }
442    
443        @Test(expected = ConfigurationException.class)
444        public void testSaveWithoutFileNameFile() throws Exception
445        {
446            FileConfiguration config = new PropertiesConfiguration();
447            config.load(TEST_FILE);
448            config.save();
449        }
450    
451        @Test(expected = ConfigurationException.class)
452        public void testSaveWithoutFileNameURL() throws Exception
453        {
454            FileConfiguration config = new PropertiesConfiguration();
455            config.load(TEST_FILE.toURI().toURL());
456            config.save();
457        }
458    
459        /**
460         * Checks that loading a directory instead of a file throws an exception.
461         */
462        @Test(expected = ConfigurationException.class)
463        public void testLoadDirectoryString() throws ConfigurationException
464        {
465            PropertiesConfiguration config = new PropertiesConfiguration();
466            config.load("target");
467        }
468    
469        /**
470         * Tests that it is not possible to load a directory using the load() method
471         * which expects a File.
472         */
473        @Test(expected = ConfigurationException.class)
474        public void testLoadDirectoryFile() throws ConfigurationException
475        {
476            PropertiesConfiguration config = new PropertiesConfiguration();
477            config.load(new File("target"));
478        }
479    
480        /**
481         * Tests that it is not possible to load a directory using the String constructor.
482         */
483        @Test(expected = ConfigurationException.class)
484        public void testLoadDirectoryConstrString() throws ConfigurationException
485        {
486            new PropertiesConfiguration("target");
487        }
488    
489        /**
490         * Tests that it is not possible to load a directory using the File constructor.
491         */
492        @Test(expected = ConfigurationException.class)
493        public void testLoadDirectoryConstrFile() throws ConfigurationException
494        {
495            new PropertiesConfiguration(new File("target"));
496        }
497    
498        /**
499         * Tests whether the constructor behaves the same as setFileName() when the
500         * configuration source is in the classpath.
501         */
502        @Test
503        public void testInitFromClassPath() throws ConfigurationException
504        {
505            PropertiesConfiguration config1 = new PropertiesConfiguration();
506            config1.setFileName(RESOURCE_NAME);
507            config1.load();
508            PropertiesConfiguration config2 = new PropertiesConfiguration(
509                    RESOURCE_NAME);
510            compare(config1, config2);
511        }
512    
513        /**
514         * Tests the loading of configuration file in a Combined configuration
515         * when the configuration source is in the classpath.
516         */
517        @Test
518        public void testLoadFromClassPath() throws ConfigurationException
519        {
520            DefaultConfigurationBuilder cf =
521                new DefaultConfigurationBuilder("config/deep/testFileFromClasspath.xml");
522            CombinedConfiguration config = cf.getConfiguration(true);
523            Configuration config1 = config.getConfiguration("propConf");
524            Configuration config2 = config.getConfiguration("propConfDeep");
525            compare(config1, config2);
526        }
527    
528        /**
529         * Tests cloning a file based configuration.
530         */
531        @Test
532        public void testClone() throws ConfigurationException
533        {
534            PropertiesConfiguration config = new PropertiesConfiguration(
535                    RESOURCE_NAME);
536            PropertiesConfiguration copy = (PropertiesConfiguration) config.clone();
537            compare(config, copy);
538            assertNull("URL was not reset", copy.getURL());
539            assertNull("Base path was not reset", copy.getBasePath());
540            assertNull("File name was not reset", copy.getFileName());
541            assertNotSame("Reloading strategy was not reset", config
542                    .getReloadingStrategy(), copy.getReloadingStrategy());
543        }
544    
545        /**
546         * Tests whether an error log listener was registered at the configuration.
547         */
548        @Test
549        public void testLogErrorListener()
550        {
551            PropertiesConfiguration config = new PropertiesConfiguration();
552            assertEquals("No error log listener registered", 1, config
553                    .getErrorListeners().size());
554        }
555    
556        /**
557         * Tests handling of errors in the reload() method.
558         */
559        @Test
560        public void testReloadError() throws ConfigurationException
561        {
562            ConfigurationErrorListenerImpl l = new ConfigurationErrorListenerImpl();
563            PropertiesConfiguration config = new PropertiesConfiguration(
564                    RESOURCE_NAME);
565            config.clearErrorListeners();
566            config.addErrorListener(l);
567            config.setReloadingStrategy(new FileAlwaysReloadingStrategy());
568            config.getString("test");
569            config.setFileName("Not existing file");
570            config.getString("test");
571            l.verify(AbstractFileConfiguration.EVENT_RELOAD, null, null);
572            assertNotNull("Exception is not set", l.getLastEvent().getCause());
573        }
574    
575        /**
576         * Tests iterating over the keys of a non hierarchical file-based
577         * configuration while a reload happens. This test is related to
578         * CONFIGURATION-347.
579         */
580        @Test
581        public void testIterationWithReloadFlat() throws ConfigurationException
582        {
583            PropertiesConfiguration config = new PropertiesConfiguration(TEST_FILE);
584            checkIterationWithReload(config);
585        }
586    
587        /**
588         * Tests iterating over the keys of a hierarchical file-based configuration
589         * while a reload happens. This test is related to CONFIGURATION-347.
590         */
591        @Test
592        public void testIterationWithReloadHierarchical()
593                throws ConfigurationException
594        {
595            XMLConfiguration config = new XMLConfiguration("test.xml");
596            checkIterationWithReload(config);
597        }
598    
599        /**
600         * Tests whether a configuration can be refreshed.
601         */
602        @Test
603        public void testRefresh() throws ConfigurationException
604        {
605            PropertiesConfiguration config = new PropertiesConfiguration(TEST_FILE);
606            assertEquals("Wrong value", 10, config.getInt("test.integer"));
607            config.setProperty("test.integer", new Integer(42));
608            assertEquals("Wrong value after update", 42,
609                    config.getInt("test.integer"));
610            config.refresh();
611            assertEquals("Wrong value after refresh", 10,
612                    config.getInt("test.integer"));
613        }
614    
615        /**
616         * Tests refresh if the configuration is not associated with a file.
617         */
618        @Test(expected = ConfigurationException.class)
619        public void testRefreshNoFile() throws ConfigurationException
620        {
621            PropertiesConfiguration config = new PropertiesConfiguration();
622            config.refresh();
623        }
624    
625        /**
626         * Helper method for testing an iteration over the keys of a file-based
627         * configuration while a reload happens.
628         *
629         * @param config the configuration to test
630         */
631        private void checkIterationWithReload(FileConfiguration config)
632        {
633            config.setReloadingStrategy(new FileAlwaysReloadingStrategy());
634            for (Iterator<String> it = config.getKeys(); it.hasNext();)
635            {
636                String key = it.next();
637                assertNotNull("No value for key " + key, config.getProperty(key));
638            }
639        }
640    
641        /**
642         * Helper method for comparing the content of two configuration objects.
643         *
644         * @param config1 the first configuration
645         * @param config2 the second configuration
646         */
647        private void compare(Configuration config1, Configuration config2)
648        {
649            StrictConfigurationComparator cc = new StrictConfigurationComparator();
650            assertTrue("Configurations are different", cc.compare(config1, config2));
651        }
652    }