    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 }