| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.configuration; |
| |
| import java.io.File; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| import org.apache.commons.collections.CollectionUtils; |
| import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy; |
| import org.apache.commons.configuration.tree.ConfigurationNode; |
| import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; |
| |
| import junit.framework.TestCase; |
| |
| /** |
| * Test case for SubnodeConfiguration. |
| * |
| * @author Oliver Heger |
| * @version $Id$ |
| */ |
| public class TestSubnodeConfiguration extends TestCase |
| { |
| /** An array with names of tables (test data). */ |
| private static final String[] TABLE_NAMES = |
| { "documents", "users" }; |
| |
| /** An array with the fields of the test tables (test data). */ |
| private static final String[][] TABLE_FIELDS = |
| { |
| { "docid", "docname", "author", "dateOfCreation", "version", "size" }, |
| { "userid", "uname", "firstName", "lastName" } }; |
| |
| /** Constant for a test output file.*/ |
| private static final File TEST_FILE = new File("target/test.xml"); |
| |
| /** Constant for an updated table name.*/ |
| private static final String NEW_TABLE_NAME = "newTable"; |
| |
| /** The parent configuration. */ |
| HierarchicalConfiguration parent; |
| |
| /** The subnode configuration to be tested. */ |
| SubnodeConfiguration config; |
| |
| /** Stores the root node of the subnode config. */ |
| ConfigurationNode subnode; |
| |
| /** Stores a counter for the created nodes. */ |
| int nodeCounter; |
| |
| protected void setUp() throws Exception |
| { |
| super.setUp(); |
| parent = setUpParentConfig(); |
| nodeCounter = 0; |
| } |
| |
| protected void tearDown() throws Exception |
| { |
| // remove the test output file if necessary |
| if (TEST_FILE.exists()) |
| { |
| TEST_FILE.delete(); |
| } |
| } |
| |
| /** |
| * Tests creation of a subnode config. |
| */ |
| public void testInitSubNodeConfig() |
| { |
| setUpSubnodeConfig(); |
| assertSame("Wrong root node in subnode", getSubnodeRoot(parent), config |
| .getRoot()); |
| assertSame("Wrong parent config", parent, config.getParent()); |
| } |
| |
| /** |
| * Tests constructing a subnode configuration with a null parent. This |
| * should cause an exception. |
| */ |
| public void testInitSubNodeConfigWithNullParent() |
| { |
| try |
| { |
| config = new SubnodeConfiguration(null, getSubnodeRoot(parent)); |
| fail("Could set a null parent config!"); |
| } |
| catch (IllegalArgumentException iex) |
| { |
| // ok |
| } |
| } |
| |
| /** |
| * Tests constructing a subnode configuration with a null root node. This |
| * should cause an exception. |
| */ |
| public void testInitSubNodeConfigWithNullNode() |
| { |
| try |
| { |
| config = new SubnodeConfiguration(parent, null); |
| fail("Could set a null root node!"); |
| } |
| catch (IllegalArgumentException iex) |
| { |
| // ok |
| } |
| } |
| |
| /** |
| * Tests if properties of the sub node can be accessed. |
| */ |
| public void testGetProperties() |
| { |
| setUpSubnodeConfig(); |
| assertEquals("Wrong table name", TABLE_NAMES[0], config |
| .getString("name")); |
| List fields = config.getList("fields.field.name"); |
| assertEquals("Wrong number of fields", TABLE_FIELDS[0].length, fields |
| .size()); |
| for (int i = 0; i < TABLE_FIELDS[0].length; i++) |
| { |
| assertEquals("Wrong field at position " + i, TABLE_FIELDS[0][i], |
| fields.get(i)); |
| } |
| } |
| |
| /** |
| * Tests setting of properties in both the parent and the subnode |
| * configuration and whether the changes are visible to each other. |
| */ |
| public void testSetProperty() |
| { |
| setUpSubnodeConfig(); |
| config.setProperty(null, "testTable"); |
| config.setProperty("name", TABLE_NAMES[0] + "_tested"); |
| assertEquals("Root value was not set", "testTable", parent |
| .getString("tables.table(0)")); |
| assertEquals("Table name was not changed", TABLE_NAMES[0] + "_tested", |
| parent.getString("tables.table(0).name")); |
| |
| parent.setProperty("tables.table(0).fields.field(1).name", "testField"); |
| assertEquals("Field name was not changed", "testField", config |
| .getString("fields.field(1).name")); |
| } |
| |
| /** |
| * Tests adding of properties. |
| */ |
| public void testAddProperty() |
| { |
| setUpSubnodeConfig(); |
| config.addProperty("[@table-type]", "test"); |
| assertEquals("parent.createNode() was not called", 1, nodeCounter); |
| assertEquals("Attribute not set", "test", parent |
| .getString("tables.table(0)[@table-type]")); |
| |
| parent.addProperty("tables.table(0).fields.field(-1).name", "newField"); |
| List fields = config.getList("fields.field.name"); |
| assertEquals("New field was not added", TABLE_FIELDS[0].length + 1, |
| fields.size()); |
| assertEquals("Wrong last field", "newField", fields |
| .get(fields.size() - 1)); |
| } |
| |
| /** |
| * Tests listing the defined keys. |
| */ |
| public void testGetKeys() |
| { |
| setUpSubnodeConfig(); |
| Set keys = new HashSet(); |
| CollectionUtils.addAll(keys, config.getKeys()); |
| assertEquals("Incorrect number of keys", 2, keys.size()); |
| assertTrue("Key 1 not contained", keys.contains("name")); |
| assertTrue("Key 2 not contained", keys.contains("fields.field.name")); |
| } |
| |
| /** |
| * Tests setting the exception on missing flag. The subnode config obtains |
| * this flag from its parent. |
| */ |
| public void testSetThrowExceptionOnMissing() |
| { |
| parent.setThrowExceptionOnMissing(true); |
| setUpSubnodeConfig(); |
| assertTrue("Exception flag not fetchted from parent", config |
| .isThrowExceptionOnMissing()); |
| try |
| { |
| config.getString("non existing key"); |
| fail("Could fetch non existing key!"); |
| } |
| catch (NoSuchElementException nex) |
| { |
| // ok |
| } |
| |
| config.setThrowExceptionOnMissing(false); |
| assertTrue("Exception flag reset on parent", parent |
| .isThrowExceptionOnMissing()); |
| } |
| |
| /** |
| * Tests handling of the delimiter parsing disabled flag. This is shared |
| * with the parent, too. |
| */ |
| public void testSetDelimiterParsingDisabled() |
| { |
| parent.setDelimiterParsingDisabled(true); |
| setUpSubnodeConfig(); |
| parent.setDelimiterParsingDisabled(false); |
| assertTrue("Delimiter parsing flag was not received from parent", |
| config.isDelimiterParsingDisabled()); |
| config.addProperty("newProp", "test1,test2,test3"); |
| assertEquals("New property was splitted", "test1,test2,test3", parent |
| .getString("tables.table(0).newProp")); |
| parent.setDelimiterParsingDisabled(true); |
| config.setDelimiterParsingDisabled(false); |
| assertTrue("Delimiter parsing flag was reset on parent", parent |
| .isDelimiterParsingDisabled()); |
| } |
| |
| /** |
| * Tests manipulating the list delimiter. This piece of data is derived from |
| * the parent. |
| */ |
| public void testSetListDelimiter() |
| { |
| parent.setListDelimiter('/'); |
| setUpSubnodeConfig(); |
| parent.setListDelimiter(';'); |
| assertEquals("List delimiter not obtained from parent", '/', config |
| .getListDelimiter()); |
| config.addProperty("newProp", "test1,test2/test3"); |
| assertEquals("List was incorrectly splitted", "test1,test2", parent |
| .getString("tables.table(0).newProp")); |
| config.setListDelimiter(','); |
| assertEquals("List delimiter changed on parent", ';', parent |
| .getListDelimiter()); |
| } |
| |
| /** |
| * Tests changing the expression engine. |
| */ |
| public void testSetExpressionEngine() |
| { |
| parent.setExpressionEngine(new XPathExpressionEngine()); |
| setUpSubnodeConfig(); |
| assertEquals("Wrong field name", TABLE_FIELDS[0][1], config |
| .getString("fields/field[2]/name")); |
| Set keys = new HashSet(); |
| CollectionUtils.addAll(keys, config.getKeys()); |
| assertEquals("Wrong number of keys", 2, keys.size()); |
| assertTrue("Key 1 not contained", keys.contains("name")); |
| assertTrue("Key 2 not contained", keys.contains("fields/field/name")); |
| config.setExpressionEngine(null); |
| assertTrue("Expression engine reset on parent", parent |
| .getExpressionEngine() instanceof XPathExpressionEngine); |
| } |
| |
| /** |
| * Tests the configurationAt() method. |
| */ |
| public void testConfiguarationAt() |
| { |
| setUpSubnodeConfig(); |
| SubnodeConfiguration sub2 = (SubnodeConfiguration) config |
| .configurationAt("fields.field(1)"); |
| assertEquals("Wrong value of property", TABLE_FIELDS[0][1], sub2 |
| .getString("name")); |
| assertEquals("Wrong parent", config.getParent(), sub2.getParent()); |
| } |
| |
| /** |
| * Tests interpolation features. The subnode config should use its parent |
| * for interpolation. |
| */ |
| public void testInterpolation() |
| { |
| parent.addProperty("tablespaces.tablespace.name", "default"); |
| parent.addProperty("tablespaces.tablespace(-1).name", "test"); |
| parent.addProperty("tables.table(0).tablespace", |
| "${tablespaces.tablespace(0).name}"); |
| assertEquals("Wrong interpolated tablespace", "default", parent |
| .getString("tables.table(0).tablespace")); |
| |
| setUpSubnodeConfig(); |
| assertEquals("Wrong interpolated tablespace in subnode", "default", |
| config.getString("tablespace")); |
| } |
| |
| /** |
| * An additional test for interpolation when the configurationAt() method is |
| * involved. |
| */ |
| public void testInterpolationFromConfigurationAt() |
| { |
| parent.addProperty("base.dir", "/home/foo"); |
| parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1"); |
| parent.addProperty("test.absolute.dir.dir2", "${base.dir}/path2"); |
| parent.addProperty("test.absolute.dir.dir3", "${base.dir}/path3"); |
| |
| Configuration sub = parent.configurationAt("test.absolute.dir"); |
| for (int i = 1; i < 4; i++) |
| { |
| assertEquals("Wrong interpolation in parent", "/home/foo/path" + i, |
| parent.getString("test.absolute.dir.dir" + i)); |
| assertEquals("Wrong interpolation in subnode", |
| "/home/foo/path" + i, sub.getString("dir" + i)); |
| } |
| } |
| |
| /** |
| * Tests a reload operation for the parent configuration when the subnode |
| * configuration does not support reloads. Then the new value should not be |
| * detected. |
| */ |
| public void testParentReloadNotSupported() throws ConfigurationException |
| { |
| Configuration c = setUpReloadTest(false); |
| assertEquals("Name changed in sub config", TABLE_NAMES[1], config |
| .getString("name")); |
| assertEquals("Name not changed in parent", NEW_TABLE_NAME, c |
| .getString("tables.table(1).name")); |
| } |
| |
| /** |
| * Tests a reload operation for the parent configuration when the subnode |
| * configuration does support reloads. The new value should be returned. |
| */ |
| public void testParentReloadSupported() throws ConfigurationException |
| { |
| Configuration c = setUpReloadTest(true); |
| assertEquals("Name not changed in sub config", NEW_TABLE_NAME, config |
| .getString("name")); |
| assertEquals("Name not changed in parent", NEW_TABLE_NAME, c |
| .getString("tables.table(1).name")); |
| } |
| |
| /** |
| * Tests a reload operation for the parent configuration when the subnode |
| * configuration is aware of reloads, and the parent configuration is |
| * accessed first. The new value should be returned. |
| */ |
| public void testParentReloadSupportAccessParent() |
| throws ConfigurationException |
| { |
| Configuration c = setUpReloadTest(true); |
| assertEquals("Name not changed in parent", NEW_TABLE_NAME, c |
| .getString("tables.table(1).name")); |
| assertEquals("Name not changed in sub config", NEW_TABLE_NAME, config |
| .getString("name")); |
| } |
| |
| /** |
| * Tests whether reloads work with sub subnode configurations. |
| */ |
| public void testParentReloadSubSubnode() throws ConfigurationException |
| { |
| setUpReloadTest(true); |
| SubnodeConfiguration sub = config.configurationAt("fields", true); |
| assertEquals("Wrong subnode key", "tables.table(1).fields", sub |
| .getSubnodeKey()); |
| assertEquals("Changed field not detected", "newField", sub |
| .getString("field(0).name")); |
| } |
| |
| /** |
| * Tests creating a sub sub config when the sub config is not aware of |
| * changes. Then the sub sub config shouldn't be either. |
| */ |
| public void testParentReloadSubSubnodeNoChangeSupport() |
| throws ConfigurationException |
| { |
| setUpReloadTest(false); |
| SubnodeConfiguration sub = config.configurationAt("fields", true); |
| assertNull("Sub sub config is attached to parent", sub.getSubnodeKey()); |
| assertEquals("Changed field name returned", TABLE_FIELDS[1][0], sub |
| .getString("field(0).name")); |
| } |
| |
| /** |
| * Prepares a test for a reload operation. |
| * |
| * @param supportReload a flag whether the subnode configuration should |
| * support reload operations |
| * @return the parent configuration that can be used for testing |
| * @throws ConfigurationException if an error occurs |
| */ |
| private XMLConfiguration setUpReloadTest(boolean supportReload) |
| throws ConfigurationException |
| { |
| XMLConfiguration xmlConf = new XMLConfiguration(parent); |
| xmlConf.setFile(TEST_FILE); |
| xmlConf.save(); |
| config = xmlConf.configurationAt("tables.table(1)", supportReload); |
| assertEquals("Wrong table name", TABLE_NAMES[1], config |
| .getString("name")); |
| xmlConf.setReloadingStrategy(new FileAlwaysReloadingStrategy()); |
| // Now change the configuration file |
| XMLConfiguration confUpdate = new XMLConfiguration(TEST_FILE); |
| confUpdate.setProperty("tables.table(1).name", NEW_TABLE_NAME); |
| confUpdate.setProperty("tables.table(1).fields.field(0).name", |
| "newField"); |
| confUpdate.save(); |
| return xmlConf; |
| } |
| |
| /** |
| * Tests a manipulation of the parent configuration that causes the subnode |
| * configuration to become invalid. In this case the sub config should be |
| * detached and keep its old values. |
| */ |
| public void testParentChangeDetach() |
| { |
| final String key = "tables.table(1)"; |
| config = parent.configurationAt(key, true); |
| assertEquals("Wrong subnode key", key, config.getSubnodeKey()); |
| assertEquals("Wrong table name", TABLE_NAMES[1], config |
| .getString("name")); |
| parent.clearTree(key); |
| assertEquals("Wrong table name after change", TABLE_NAMES[1], config |
| .getString("name")); |
| assertNull("Sub config was not detached", config.getSubnodeKey()); |
| } |
| |
| /** |
| * Tests detaching a subnode configuration when an exception is thrown |
| * during reconstruction. This can happen e.g. if the expression engine is |
| * changed for the parent. |
| */ |
| public void testParentChangeDetatchException() |
| { |
| config = parent.configurationAt("tables.table(1)", true); |
| parent.setExpressionEngine(new XPathExpressionEngine()); |
| assertEquals("Wrong name of table", TABLE_NAMES[1], config |
| .getString("name")); |
| assertNull("Sub config was not detached", config.getSubnodeKey()); |
| } |
| |
| /** |
| * Initializes the parent configuration. This method creates the typical |
| * structure of tables and fields nodes. |
| * |
| * @return the parent configuration |
| */ |
| protected HierarchicalConfiguration setUpParentConfig() |
| { |
| HierarchicalConfiguration conf = new HierarchicalConfiguration() |
| { |
| // Provide a special implementation of createNode() to check |
| // if it is called by the subnode config |
| protected Node createNode(String name) |
| { |
| nodeCounter++; |
| return super.createNode(name); |
| } |
| }; |
| for (int i = 0; i < TABLE_NAMES.length; i++) |
| { |
| conf.addProperty("tables.table(-1).name", TABLE_NAMES[i]); |
| for (int j = 0; j < TABLE_FIELDS[i].length; j++) |
| { |
| conf.addProperty("tables.table.fields.field(-1).name", |
| TABLE_FIELDS[i][j]); |
| } |
| } |
| return conf; |
| } |
| |
| /** |
| * Returns the root node for the subnode config. This method returns the |
| * first table node. |
| * |
| * @param conf the parent config |
| * @return the root node for the subnode config |
| */ |
| protected ConfigurationNode getSubnodeRoot(HierarchicalConfiguration conf) |
| { |
| ConfigurationNode root = conf.getRoot(); |
| return root.getChild(0).getChild(0); |
| } |
| |
| /** |
| * Performs a standard initialization of the subnode config to test. |
| */ |
| protected void setUpSubnodeConfig() |
| { |
| config = new SubnodeConfiguration(parent, getSubnodeRoot(parent)); |
| } |
| } |