1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration;
18  
19  import java.io.File;
20  import java.util.Collection;
21  import java.util.Set;
22  
23  import org.apache.commons.configuration.beanutils.BeanHelper;
24  import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
25  import org.apache.commons.configuration.tree.DefaultConfigurationNode;
26  import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
27  
28  import junit.framework.TestCase;
29  
30  /***
31   * Test class for DefaultConfigurationBuilder.
32   *
33   * @author Oliver Heger
34   * @version $Id: TestDefaultConfigurationBuilder.java 384601 2006-03-09
35   * 20:22:58Z oheger $
36   */
37  public class TestDefaultConfigurationBuilder extends TestCase
38  {
39      /*** Test configuration definition file. */
40      private static final File TEST_FILE = new File(
41              "conf/testDigesterConfiguration.xml");
42  
43      private static final File ADDITIONAL_FILE = new File(
44              "conf/testDigesterConfiguration2.xml");
45  
46      private static final File OPTIONAL_FILE = new File(
47              "conf/testDigesterOptionalConfiguration.xml");
48  
49      private static final File OPTIONALEX_FILE = new File(
50              "conf/testDigesterOptionalConfigurationEx.xml");
51  
52      private static final File MULTI_FILE = new File(
53              "conf/testDigesterConfiguration3.xml");
54  
55      private static final File INIT_FILE = new File(
56              "conf/testComplexInitialization.xml");
57  
58      /*** Stores the object to be tested. */
59      DefaultConfigurationBuilder factory;
60  
61      protected void setUp() throws Exception
62      {
63          super.setUp();
64          System
65                  .setProperty("java.naming.factory.initial",
66                          "org.apache.commons.configuration.MockStaticMemoryInitialContextFactory");
67          System.setProperty("test_file_xml", "test.xml");
68          System.setProperty("test_file_combine", "testcombine1.xml");
69          factory = new DefaultConfigurationBuilder();
70      }
71  
72      /***
73       * Tests the isReservedNode() method of ConfigurationDeclaration.
74       */
75      public void testConfigurationDeclarationIsReserved()
76      {
77          DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
78                  factory, factory);
79          DefaultConfigurationNode parent = new DefaultConfigurationNode();
80          DefaultConfigurationNode nd = new DefaultConfigurationNode("at");
81          parent.addAttribute(nd);
82          assertTrue("Attribute at not recognized", decl.isReservedNode(nd));
83          nd = new DefaultConfigurationNode("optional");
84          parent.addAttribute(nd);
85          assertTrue("Attribute optional not recognized", decl.isReservedNode(nd));
86          nd = new DefaultConfigurationNode("config-class");
87          parent.addAttribute(nd);
88          assertTrue("Inherited attribute not recognized", decl
89                  .isReservedNode(nd));
90          nd = new DefaultConfigurationNode("different");
91          parent.addAttribute(nd);
92          assertFalse("Wrong reserved attribute", decl.isReservedNode(nd));
93          nd = new DefaultConfigurationNode("at");
94          parent.addChild(nd);
95          assertFalse("Node type not evaluated", decl.isReservedNode(nd));
96      }
97  
98      /***
99       * Tests if the at attribute is correctly detected as reserved attribute.
100      */
101     public void testConfigurationDeclarationIsReservedAt()
102     {
103         checkOldReservedAttribute("at");
104     }
105 
106     /***
107      * Tests if the optional attribute is correctly detected as reserved
108      * attribute.
109      */
110     public void testConfigurationDeclarationIsReservedOptional()
111     {
112         checkOldReservedAttribute("optional");
113     }
114 
115     /***
116      * Tests if special reserved attributes are recognized by the
117      * isReservedNode() method. For compatibility reasons the attributes "at"
118      * and "optional" are also treated as reserved attributes, but only if there
119      * are no corresponding attributes with the "config-" prefix.
120      *
121      * @param name the attribute name
122      */
123     private void checkOldReservedAttribute(String name)
124     {
125         DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
126                 factory, factory);
127         DefaultConfigurationNode parent = new DefaultConfigurationNode();
128         DefaultConfigurationNode nd = new DefaultConfigurationNode("config-"
129                 + name);
130         parent.addAttribute(nd);
131         assertTrue("config-" + name + " attribute not recognized", decl
132                 .isReservedNode(nd));
133         DefaultConfigurationNode nd2 = new DefaultConfigurationNode(name);
134         parent.addAttribute(nd2);
135         assertFalse(name + " is reserved though config- exists", decl
136                 .isReservedNode(nd2));
137         assertTrue("config- attribute not recognized when " + name + " exists",
138                 decl.isReservedNode(nd));
139     }
140 
141     /***
142      * Tests access to certain reserved attributes of a
143      * ConfigurationDeclaration.
144      */
145     public void testConfigurationDeclarationGetAttributes()
146     {
147         factory.addProperty("/ xml/fileName", "test.xml");
148         DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
149                 factory, factory.configurationAt("xml"));
150         assertNull("Found an at attribute", decl.getAt());
151         assertFalse("Found an optional attribute", decl.isOptional());
152         factory.addProperty("/xml @config-at", "test1");
153         assertEquals("Wrong value of at attribute", "test1", decl.getAt());
154         factory.addProperty("/xml @at", "test2");
155         assertEquals("Wrong value of config-at attribute", "test1", decl.getAt());
156         factory.clearProperty("/xml/@config-at");
157         assertEquals("Old at attribute not detected", "test2", decl.getAt());
158         factory.addProperty("/xml @config-optional", "true");
159         assertTrue("Wrong value of optional attribute", decl.isOptional());
160         factory.addProperty("/xml @optional", "false");
161         assertTrue("Wrong value of config-optional attribute", decl.isOptional());
162         factory.clearProperty("/xml/@config-optional");
163         factory.setProperty("/xml/@optional", Boolean.TRUE);
164         assertTrue("Old optional attribute not detected", decl.isOptional());
165         factory.setProperty("/xml/@optional", "invalid value");
166         try
167         {
168             decl.isOptional();
169             fail("Invalid optional attribute was not detected!");
170         }
171         catch (ConfigurationRuntimeException crex)
172         {
173             // ok
174         }
175     }
176 
177     /***
178      * Tests adding a new configuration provider.
179      */
180     public void testAddConfigurationProvider()
181     {
182         DefaultConfigurationBuilder.ConfigurationProvider provider = new DefaultConfigurationBuilder.ConfigurationProvider();
183         assertNull("Provider already registered", factory
184                 .providerForTag("test"));
185         factory.addConfigurationProvider("test", provider);
186         assertSame("Provider not registered", provider, factory
187                 .providerForTag("test"));
188     }
189 
190     /***
191      * Tries to register a null configuration provider. This should cause an
192      * exception.
193      */
194     public void testAddConfigurationProviderNull()
195     {
196         try
197         {
198             factory.addConfigurationProvider("test", null);
199             fail("Could register null provider");
200         }
201         catch (IllegalArgumentException iex)
202         {
203             // ok
204         }
205     }
206 
207     /***
208      * Tries to register a configuration provider for a null tag. This should
209      * cause an exception to be thrown.
210      */
211     public void testAddConfigurationProviderNullTag()
212     {
213         try
214         {
215             factory.addConfigurationProvider(null,
216                     new DefaultConfigurationBuilder.ConfigurationProvider());
217             fail("Could register provider for null tag!");
218         }
219         catch (IllegalArgumentException iex)
220         {
221             // ok
222         }
223     }
224 
225     /***
226      * Tests removing configuration providers.
227      */
228     public void testRemoveConfigurationProvider()
229     {
230         assertNull("Removing unknown provider", factory
231                 .removeConfigurationProvider("test"));
232         assertNull("Removing provider for null tag", factory
233                 .removeConfigurationProvider(null));
234         DefaultConfigurationBuilder.ConfigurationProvider provider = new DefaultConfigurationBuilder.ConfigurationProvider();
235         factory.addConfigurationProvider("test", provider);
236         assertSame("Failed to remove provider", provider, factory
237                 .removeConfigurationProvider("test"));
238         assertNull("Provider still registered", factory.providerForTag("test"));
239     }
240 
241     /***
242      * Tests creating a configuration object from a configuration declaration.
243      */
244     public void testConfigurationBeanFactoryCreateBean()
245     {
246         factory.addConfigurationProvider("test",
247                 new DefaultConfigurationBuilder.ConfigurationProvider(
248                         PropertiesConfiguration.class));
249         factory.addProperty("/ test@throwExceptionOnMissing", "true");
250         DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
251                 factory, factory.configurationAt("test"));
252         PropertiesConfiguration conf = (PropertiesConfiguration) BeanHelper
253                 .createBean(decl);
254         assertTrue("Property was not initialized", conf
255                 .isThrowExceptionOnMissing());
256     }
257 
258     /***
259      * Tests creating a configuration object from an unknown tag. This should
260      * cause an exception.
261      */
262     public void testConfigurationBeanFactoryCreateUnknownTag()
263     {
264         factory.addProperty("/ test@throwExceptionOnMissing", "true");
265         DefaultConfigurationBuilder.ConfigurationDeclaration decl = new DefaultConfigurationBuilder.ConfigurationDeclaration(
266                 factory, factory.configurationAt("test"));
267         try
268         {
269             BeanHelper.createBean(decl);
270             fail("Could create configuration from unknown tag!");
271         }
272         catch (ConfigurationRuntimeException crex)
273         {
274             // ok
275         }
276     }
277 
278     /***
279      * Tests loading a simple configuration definition file.
280      */
281     public void testLoadConfiguration() throws ConfigurationException
282     {
283         factory.setFile(TEST_FILE);
284         checkConfiguration();
285     }
286 
287     /***
288      * Tests the file constructor.
289      */
290     public void testLoadConfigurationFromFile() throws ConfigurationException
291     {
292         factory = new DefaultConfigurationBuilder(TEST_FILE);
293         checkConfiguration();
294     }
295 
296     /***
297      * Tests the file name constructor.
298      */
299     public void testLoadConfigurationFromFileName()
300             throws ConfigurationException
301     {
302         factory = new DefaultConfigurationBuilder(TEST_FILE.getAbsolutePath());
303         checkConfiguration();
304     }
305 
306     /***
307      * Tests the URL constructor.
308      */
309     public void testLoadConfigurationFromURL() throws Exception
310     {
311         factory = new DefaultConfigurationBuilder(TEST_FILE.toURL());
312         checkConfiguration();
313     }
314 
315     /***
316      * Tests if the configuration was correctly created by the factory.
317      */
318     private void checkConfiguration() throws ConfigurationException
319     {
320         CombinedConfiguration compositeConfiguration = (CombinedConfiguration) factory
321                 .getConfiguration();
322 
323         assertEquals("Number of configurations", 3, compositeConfiguration
324                 .getNumberOfConfigurations());
325         assertEquals(PropertiesConfiguration.class, compositeConfiguration
326                 .getConfiguration(0).getClass());
327         assertEquals(XMLPropertiesConfiguration.class, compositeConfiguration
328                 .getConfiguration(1).getClass());
329         assertEquals(XMLConfiguration.class, compositeConfiguration
330                 .getConfiguration(2).getClass());
331 
332         // check the first configuration
333         PropertiesConfiguration pc = (PropertiesConfiguration) compositeConfiguration
334                 .getConfiguration(0);
335         assertNotNull("Make sure we have a fileName: " + pc.getFileName(), pc
336                 .getFileName());
337 
338         // check some properties
339         checkProperties(compositeConfiguration);
340     }
341 
342     /***
343      * Checks if the passed in configuration contains the expected properties.
344      *
345      * @param compositeConfiguration the configuration to check
346      */
347     private void checkProperties(Configuration compositeConfiguration)
348     {
349         assertTrue("Make sure we have loaded our key", compositeConfiguration
350                 .getBoolean("test.boolean"));
351         assertEquals("I'm complex!", compositeConfiguration
352                 .getProperty("element2.subelement.subsubelement"));
353         assertEquals("property in the XMLPropertiesConfiguration", "value1",
354                 compositeConfiguration.getProperty("key1"));
355     }
356 
357     /***
358      * Tests loading a configuration definition file with an additional section.
359      */
360     public void testLoadAdditional() throws ConfigurationException
361     {
362         factory.setFile(ADDITIONAL_FILE);
363         CombinedConfiguration compositeConfiguration = (CombinedConfiguration) factory
364                 .getConfiguration();
365         assertEquals("Verify how many configs", 2, compositeConfiguration
366                 .getNumberOfConfigurations());
367 
368         // Test if union was constructed correctly
369         Object prop = compositeConfiguration.getProperty("tables.table.name");
370         assertTrue(prop instanceof Collection);
371         assertEquals(3, ((Collection) prop).size());
372         assertEquals("users", compositeConfiguration
373                 .getProperty("tables.table(0).name"));
374         assertEquals("documents", compositeConfiguration
375                 .getProperty("tables.table(1).name"));
376         assertEquals("tasks", compositeConfiguration
377                 .getProperty("tables.table(2).name"));
378 
379         prop = compositeConfiguration
380                 .getProperty("tables.table.fields.field.name");
381         assertTrue(prop instanceof Collection);
382         assertEquals(17, ((Collection) prop).size());
383 
384         assertEquals("smtp.mydomain.org", compositeConfiguration
385                 .getString("mail.host.smtp"));
386         assertEquals("pop3.mydomain.org", compositeConfiguration
387                 .getString("mail.host.pop"));
388 
389         // This was overriden
390         assertEquals("masterOfPost", compositeConfiguration
391                 .getString("mail.account.user"));
392         assertEquals("topsecret", compositeConfiguration
393                 .getString("mail.account.psswd"));
394 
395         // This was overriden, too, but not in additional section
396         assertEquals("enhanced factory", compositeConfiguration
397                 .getString("test.configuration"));
398     }
399 
400     /***
401      * Tests loading a definition file that contains optional configurations.
402      */
403     public void testLoadOptional() throws Exception
404     {
405         factory.setURL(OPTIONAL_FILE.toURL());
406         Configuration config = factory.getConfiguration();
407         assertTrue(config.getBoolean("test.boolean"));
408         assertEquals("value", config.getProperty("element"));
409     }
410 
411     /***
412      * Tests loading a definition file with optional and non optional
413      * configuration sources. One non optional does not exist, so this should
414      * cause an exception.
415      */
416     public void testLoadOptionalWithException()
417     {
418         factory.setFile(OPTIONALEX_FILE);
419         try
420         {
421             factory.getConfiguration();
422             fail("Non existing source did not cause an exception!");
423         }
424         catch (ConfigurationException cex)
425         {
426             // ok
427         }
428     }
429 
430     /***
431      * Tries to load a configuration file with an optional, non file-based
432      * configuration. The optional attribute should work for other configuration
433      * classes, too.
434      */
435     public void testLoadOptionalNonFileBased() throws ConfigurationException
436     {
437         factory.addProperty("/ override/configuration@fileName",
438                 "nonExisting.xml");
439         factory.addProperty("/override/configuration[1] @config-optional",
440                 Boolean.TRUE);
441         factory.addProperty("/override/configuration[1] @config-name",
442                 "optionalConfig");
443         CombinedConfiguration config = factory.getConfiguration(false);
444         assertTrue("Configuration not empty", config.isEmpty());
445         assertEquals("Wrong number of configurations", 0, config
446                 .getNumberOfConfigurations());
447     }
448 
449     /***
450      * Tests loading a definition file with multiple different sources.
451      */
452     public void testLoadDifferentSources() throws ConfigurationException
453     {
454         factory.setFile(MULTI_FILE);
455         Configuration config = factory.getConfiguration();
456         assertFalse(config.isEmpty());
457         assertTrue(config instanceof CombinedConfiguration);
458         CombinedConfiguration cc = (CombinedConfiguration) config;
459         assertEquals("Wrong number of configurations", 1, cc
460                 .getNumberOfConfigurations());
461 
462         assertNotNull(config
463                 .getProperty("tables.table(0).fields.field(2).name"));
464         assertNotNull(config.getProperty("element2.subelement.subsubelement"));
465         assertEquals("value", config.getProperty("element3"));
466         assertEquals("foo", config.getProperty("element3[@name]"));
467         assertNotNull(config.getProperty("mail.account.user"));
468 
469         // test JNDIConfiguration
470         assertNotNull(config.getProperty("test.onlyinjndi"));
471         assertTrue(config.getBoolean("test.onlyinjndi"));
472 
473         Configuration subset = config.subset("test");
474         assertNotNull(subset.getProperty("onlyinjndi"));
475         assertTrue(subset.getBoolean("onlyinjndi"));
476 
477         // test SystemConfiguration
478         assertNotNull(config.getProperty("java.version"));
479         assertEquals(System.getProperty("java.version"), config
480                 .getString("java.version"));
481     }
482 
483     /***
484      * Tests if the base path is correctly evaluated.
485      */
486     public void testSetConfigurationBasePath() throws ConfigurationException
487     {
488         factory.addProperty("/ properties@fileName", "test.properties");
489         File deepDir = new File("conf/config/deep");
490         factory.setConfigurationBasePath(deepDir.getAbsolutePath());
491 
492         Configuration config = factory.getConfiguration(false);
493         assertEquals("Wrong property value", "somevalue", config
494                 .getString("somekey"));
495     }
496 
497     /***
498      * Tests reading a configuration definition file that contains complex
499      * initialization of properties of the declared configuration sources.
500      */
501     public void testComplexInitialization() throws ConfigurationException
502     {
503         factory.setFile(INIT_FILE);
504         CombinedConfiguration cc = (CombinedConfiguration) factory
505                 .getConfiguration();
506 
507         assertEquals("System property not found", "test.xml",
508                 cc.getString("test_file_xml"));
509         PropertiesConfiguration c1 = (PropertiesConfiguration) cc
510                 .getConfiguration(1);
511         assertTrue(
512                 "Reloading strategy was not set",
513                 c1.getReloadingStrategy() instanceof FileChangedReloadingStrategy);
514         assertEquals("Refresh delay was not set", 10000,
515                 ((FileChangedReloadingStrategy) c1.getReloadingStrategy())
516                         .getRefreshDelay());
517 
518         Configuration xmlConf = cc.getConfiguration("xml");
519         assertEquals("Property not found", "I'm complex!", xmlConf
520                 .getString("element2/subelement/subsubelement"));
521         assertEquals("List index not found", "two", xmlConf
522                 .getString("list[0]/item[1]"));
523         assertEquals("Property in combiner file not found", "yellow", cc
524                 .getString("/gui/selcolor"));
525 
526         assertTrue("Delimiter flag was not set", cc
527                 .isDelimiterParsingDisabled());
528         assertTrue("Expression engine was not set",
529                 cc.getExpressionEngine() instanceof XPathExpressionEngine);
530     }
531 
532     /***
533      * Tests if the returned combined configuration has the expected structure.
534      */
535     public void testCombinedConfiguration() throws ConfigurationException
536     {
537         factory.setFile(INIT_FILE);
538         CombinedConfiguration cc = (CombinedConfiguration) factory
539                 .getConfiguration();
540         assertNotNull("Properties configuration not found", cc
541                 .getConfiguration("properties"));
542         assertNotNull("XML configuration not found", cc.getConfiguration("xml"));
543         assertEquals("Wrong number of contained configs", 4, cc
544                 .getNumberOfConfigurations());
545 
546         CombinedConfiguration cc2 = (CombinedConfiguration) cc
547                 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME);
548         assertNotNull("No additional configuration found", cc2);
549         Set names = cc2.getConfigurationNames();
550         assertEquals("Wrong number of contained additional configs", 2, names
551                 .size());
552         assertTrue("Config 1 not contained", names.contains("combiner1"));
553         assertTrue("Config 2 not contained", names.contains("combiner2"));
554     }
555 
556     /***
557      * Tests the structure of the returned combined configuration if there is no
558      * additional section.
559      */
560     public void testCombinedConfigurationNoAdditional()
561             throws ConfigurationException
562     {
563         factory.setFile(TEST_FILE);
564         CombinedConfiguration cc = factory.getConfiguration(true);
565         assertNull("Additional configuration was found", cc
566                 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME));
567     }
568 
569     /***
570      * Tests whether the list node definition was correctly processed.
571      */
572     public void testCombinedConfigurationListNodes()
573             throws ConfigurationException
574     {
575         factory.setFile(INIT_FILE);
576         CombinedConfiguration cc = factory.getConfiguration(true);
577         Set listNodes = cc.getNodeCombiner().getListNodes();
578         assertEquals("Wrong number of list nodes", 2, listNodes.size());
579         assertTrue("table node not a list node", listNodes.contains("table"));
580         assertTrue("list node not a list node", listNodes.contains("list"));
581 
582         CombinedConfiguration cca = (CombinedConfiguration) cc
583                 .getConfiguration(DefaultConfigurationBuilder.ADDITIONAL_NAME);
584         listNodes = cca.getNodeCombiner().getListNodes();
585         assertTrue("Found list nodes for additional combiner", listNodes
586                 .isEmpty());
587     }
588 
589     /***
590      * Tests whether a configuration builder can itself be declared in a
591      * configuration definition file.
592      */
593     public void testConfigurationBuilderProvider()
594             throws ConfigurationException
595     {
596         factory.addProperty("/ override/configuration@fileName", TEST_FILE
597                 .getAbsolutePath());
598         CombinedConfiguration cc = factory.getConfiguration(false);
599         assertEquals("Wrong number of configurations", 1, cc
600                 .getNumberOfConfigurations());
601         checkProperties(cc);
602     }
603 }