You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2021/08/25 15:41:49 UTC

[commons-configuration] branch master updated: Sort test members.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-configuration.git


The following commit(s) were added to refs/heads/master by this push:
     new 66b4f4e  Sort test members.
66b4f4e is described below

commit 66b4f4eb73b276d041debaad80af3074ccb359bd
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 25 11:41:46 2021 -0400

    Sort test members.
---
 .../configuration2/BaseNonStringProperties.java    |   68 +-
 .../configuration2/ConfigurationAssert.java        |   78 +-
 .../DatabaseConfigurationTestHelper.java           |  196 +-
 .../org/apache/commons/configuration2/Logging.java |  112 +-
 .../configuration2/MockInitialContextFactory.java  |  148 +-
 .../configuration2/NonCloneableConfiguration.java  |   12 +-
 .../configuration2/NonStringTestHolder.java        |   18 +-
 .../configuration2/SynchronizerTestImpl.java       |   94 +-
 .../configuration2/TestAbstractConfiguration.java  |  112 +-
 .../TestAbstractConfigurationBasicFeatures.java    | 1154 ++++----
 .../TestAbstractConfigurationSynchronization.java  |  174 +-
 .../TestAbstractHierarchicalConfiguration.java     |  986 +++----
 .../TestBaseConfigurationXMLReader.java            |  112 +-
 ...seHierarchicalConfigurationSynchronization.java |  280 +-
 .../configuration2/TestBaseNullConfiguration.java  |  334 +--
 .../configuration2/TestCatalogResolver.java        |   48 +-
 .../configuration2/TestCombinedConfiguration.java  | 1234 ++++-----
 .../configuration2/TestCompositeConfiguration.java |  938 +++----
 .../configuration2/TestConfigurationConverter.java |   46 +-
 .../configuration2/TestConfigurationLookup.java    |   32 +-
 .../configuration2/TestConfigurationSet.java       |   10 +-
 .../configuration2/TestConfigurationUtils.java     |  532 ++--
 .../configuration2/TestDataConfiguration.java      | 2742 ++++++++++----------
 .../configuration2/TestDatabaseConfiguration.java  |  460 ++--
 .../TestDynamicCombinedConfiguration.java          |  508 ++--
 .../TestEnvironmentConfiguration.java              |   48 +-
 .../commons/configuration2/TestEqualBehavior.java  |  122 +-
 .../TestHierarchicalConfiguration.java             |  540 ++--
 .../TestHierarchicalXMLConfiguration.java          |  176 +-
 .../configuration2/TestINIConfiguration.java       | 1084 ++++----
 .../configuration2/TestImmutableConfiguration.java |  120 +-
 .../configuration2/TestJNDIConfiguration.java      |  338 +--
 .../configuration2/TestJNDIEnvironmentValues.java  |   90 +-
 .../configuration2/TestMapConfiguration.java       |   54 +-
 .../TestNullCompositeConfiguration.java            |  414 +--
 .../TestNullJNDIEnvironmentValues.java             |   90 +-
 .../TestPropertiesConfiguration.java               |  242 +-
 .../TestPropertiesConfigurationLayout.java         |  776 +++---
 .../configuration2/TestSubnodeConfiguration.java   |  348 +--
 .../configuration2/TestSubsetConfiguration.java    |  254 +-
 .../configuration2/TestSystemConfiguration.java    |   88 +-
 .../configuration2/TestXMLConfiguration_605.java   |   32 +-
 .../configuration2/TestXMLDocumentHelper.java      |  264 +-
 .../configuration2/TestXMLListHandling.java        |  128 +-
 .../TestXMLPropertiesConfiguration.java            |   58 +-
 .../configuration2/TestYAMLConfiguration.java      |   92 +-
 .../beanutils/BeanCreationTestBean.java            |   16 +-
 .../BeanCreationTestBeanWithListChild.java         |   16 +-
 .../beanutils/BeanDeclarationTestImpl.java         |   38 +-
 .../configuration2/beanutils/TestBeanHelper.java   |  530 ++--
 .../beanutils/TestCombinedBeanDeclaration.java     |  138 +-
 .../beanutils/TestConfigurationDynaBean.java       |  248 +-
 .../beanutils/TestConstructorArg.java              |   36 +-
 .../beanutils/TestDefaultBeanFactory.java          |  144 +-
 .../beanutils/TestXMLBeanDeclaration.java          |  338 +--
 .../builder/BuilderEventListenerImpl.java          |   38 +-
 .../builder/ParametersBeanTestImpl.java            |   24 +-
 .../builder/TestAutoSaveListener.java              |   80 +-
 .../builder/TestBasicBuilderParameters.java        |  510 ++--
 .../builder/TestBasicConfigurationBuilder.java     |  560 ++--
 .../TestBasicConfigurationBuilderEvents.java       |   96 +-
 .../TestBuilderConfigurationWrapperFactory.java    |   76 +-
 .../builder/TestCopyObjectDefaultHandler.java      |   48 +-
 .../builder/TestDatabaseBuilderParametersImpl.java |   84 +-
 .../builder/TestDefaultParametersManager.java      |  120 +-
 .../builder/TestEventListenerParameters.java       |   36 +-
 .../builder/TestFileBasedBuilderParameters.java    |  348 +--
 .../builder/TestFileBasedConfigurationBuilder.java |  604 ++---
 .../TestHierarchicalBuilderParametersImpl.java     |   22 +-
 .../builder/TestJndiBuilderParametersImpl.java     |   42 +-
 .../TestPropertiesBuilderParametersImpl.java       |   96 +-
 ...TestReloadingFileBasedConfigurationBuilder.java |  178 +-
 .../builder/TestXMLBuilderParametersImpl.java      |  108 +-
 .../AbstractMultiFileConfigurationBuilderTest.java |   52 +-
 .../TestBaseConfigurationBuilderProvider.java      |  156 +-
 .../TestCombinedBuilderParametersImpl.java         |  310 +--
 .../combined/TestCombinedConfigurationBuilder.java | 1280 ++++-----
 .../combined/TestConfigurationDeclaration.java     |   56 +-
 ...tFileExtensionConfigurationBuilderProvider.java |   94 +-
 .../TestMultiFileBuilderParametersImpl.java        |   74 +-
 .../TestMultiFileConfigurationBuilder.java         |  306 +--
 .../builder/combined/TestMultiWrapDynaBean.java    |  190 +-
 .../TestReloadingCombinedConfigurationBuilder.java |   60 +-
 ...adingCombinedConfigurationBuilderFileBased.java |  306 +--
 ...TestReloadingMultiFileConfigurationBuilder.java |  106 +-
 .../builder/fluent/TestConfigurations.java         |  402 +--
 .../builder/fluent/TestParameters.java             |  278 +-
 .../convert/TestDefaultConversionHandler.java      |  244 +-
 .../convert/TestDefaultListDelimiterHandler.java   |  110 +-
 .../convert/TestDisabledListDelimiterHandler.java  |  140 +-
 .../convert/TestPropertyConverter.java             |  202 +-
 .../event/AbstractEventListenerTestImpl.java       |   24 +-
 .../event/AbstractTestConfigurationEvents.java     |   64 +-
 .../event/TestConfigurationEventTypes.java         |  164 +-
 .../event/TestDatabaseConfigurationEvents.java     |   18 +-
 .../event/TestEventListenerList.java               |  608 ++---
 .../configuration2/event/TestEventSource.java      |  324 +--
 .../event/TestHierarchicalConfigurationEvents.java |   56 +-
 .../io/TestAbsoluteNameLocationStrategy.java       |   26 +-
 .../io/TestBasePathLocationStrategy.java           |   30 +-
 .../io/TestClasspathLocationStrategy.java          |   26 +-
 .../io/TestCombinedLocationStrategy.java           |  102 +-
 .../configuration2/io/TestConfigurationLogger.java |  168 +-
 .../configuration2/io/TestDefaultFileSystem.java   |   16 +-
 .../commons/configuration2/io/TestFileHandler.java | 1566 +++++------
 .../commons/configuration2/io/TestFileLocator.java |   66 +-
 .../configuration2/io/TestFileLocatorUtils.java    |  430 +--
 .../io/TestHomeDirectoryLocationStrategy.java      |   22 +-
 .../io/TestProvidedURLLocationStrategy.java        |   18 +-
 .../plist/TestPropertyListConfiguration.java       |  348 +--
 .../plist/TestPropertyListParser.java              |   38 +-
 .../plist/TestXMLPropertyListConfiguration.java    |  378 +--
 .../reloading/TestCombinedReloadingController.java |  106 +-
 .../TestFileHandlerReloadingDetector.java          |  176 +-
 .../reloading/TestManagedReloadingDetector.java    |   16 +-
 .../reloading/TestPeriodicReloadingTrigger.java    |   98 +-
 .../reloading/TestReloadingController.java         |  130 +-
 .../TestVFSFileHandlerReloadingDetector.java       |   76 +-
 .../TestConfigurationPropertiesFactoryBean.java    |   86 +-
 .../spring/TestConfigurationPropertySource.java    |   22 +-
 .../sync/TestReadWriteSynchronizer.java            |  210 +-
 .../apache/commons/configuration2/test/HsqlDB.java |   38 +-
 .../configuration2/tree/AbstractCombinerTest.java  |   26 +-
 .../tree/AbstractImmutableNodeHandlerTest.java     |  240 +-
 .../configuration2/tree/NodeStructureHelper.java   |  460 ++--
 .../tree/TestDefaultConfigurationKey.java          |  474 ++--
 .../tree/TestDefaultExpressionEngine.java          |  738 +++---
 .../tree/TestDefaultExpressionEngineSymbols.java   |   46 +-
 .../configuration2/tree/TestImmutableNode.java     |  506 ++--
 .../configuration2/tree/TestInMemoryNodeModel.java |  672 ++---
 .../tree/TestInMemoryNodeModelReferences.java      |  172 +-
 .../tree/TestInMemoryNodeModelTrackedNodes.java    |  738 +++---
 .../configuration2/tree/TestMergeCombiner.java     |   86 +-
 .../configuration2/tree/TestNodeAddData.java       |   36 +-
 .../configuration2/tree/TestNodeNameMatchers.java  |   66 +-
 .../configuration2/tree/TestNodeSelector.java      |  120 +-
 .../configuration2/tree/TestNodeTreeWalker.java    |  256 +-
 .../configuration2/tree/TestNodeUpdateData.java    |   88 +-
 .../configuration2/tree/TestOverrideCombiner.java  |  142 +-
 .../configuration2/tree/TestQueryResult.java       |   86 +-
 .../tree/TestTrackedNodeHandler.java               |   26 +-
 .../configuration2/tree/TestTrackedNodeModel.java  |  172 +-
 .../configuration2/tree/TestUnionCombiner.java     |   46 +-
 .../tree/xpath/AbstractXPathTest.java              |   88 +-
 .../xpath/TestConfigurationAttributePointer.java   |   54 +-
 .../xpath/TestConfigurationIteratorAttributes.java |   40 +-
 .../TestConfigurationNodeIteratorChildren.java     |  204 +-
 .../tree/xpath/TestConfigurationNodePointer.java   |   82 +-
 .../xpath/TestConfigurationNodePointerFactory.java |   90 +-
 .../tree/xpath/TestXPathExpressionEngine.java      |  410 +--
 .../xpath/TestXPathExpressionEngineInConfig.java   |   78 +-
 .../web/TestAppletConfiguration.java               |   32 +-
 .../web/TestServletFilterConfiguration.java        |   46 +-
 .../web/TestServletRequestConfiguration.java       |   48 +-
 154 files changed, 18272 insertions(+), 18272 deletions(-)

diff --git a/src/test/java/org/apache/commons/configuration2/BaseNonStringProperties.java b/src/test/java/org/apache/commons/configuration2/BaseNonStringProperties.java
index 93ed6c9..ff66e08 100644
--- a/src/test/java/org/apache/commons/configuration2/BaseNonStringProperties.java
+++ b/src/test/java/org/apache/commons/configuration2/BaseNonStringProperties.java
@@ -37,11 +37,6 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testBooleanDefaultValue() throws Exception {
-        nonStringTestHolder.testBooleanDefaultValue();
-    }
-
-    @Test
     public void testBooleanArrayValue() throws Exception {
         final boolean booleanValue = conf.getBoolean("test.boolean");
         assertTrue(booleanValue);
@@ -49,6 +44,11 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
+    public void testBooleanDefaultValue() throws Exception {
+        nonStringTestHolder.testBooleanDefaultValue();
+    }
+
+    @Test
     public void testByte() throws Exception {
         nonStringTestHolder.testByte();
     }
@@ -67,11 +67,6 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testDoubleDefaultValue() throws Exception {
-        nonStringTestHolder.testDoubleDefaultValue();
-    }
-
-    @Test
     public void testDoubleArrayValue() throws Exception {
         final double testValue = 10.25;
         final double doubleValue = conf.getDouble("test.double");
@@ -80,14 +75,13 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testFloat() throws Exception {
-        nonStringTestHolder.testFloat();
+    public void testDoubleDefaultValue() throws Exception {
+        nonStringTestHolder.testDoubleDefaultValue();
     }
 
     @Test
-    public void testFloatDefaultValue() throws Exception {
-        nonStringTestHolder.testFloatDefaultValue();
-
+    public void testFloat() throws Exception {
+        nonStringTestHolder.testFloat();
     }
 
     @Test
@@ -99,13 +93,14 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testInteger() throws Exception {
-        nonStringTestHolder.testInteger();
+    public void testFloatDefaultValue() throws Exception {
+        nonStringTestHolder.testFloatDefaultValue();
+
     }
 
     @Test
-    public void testIntegerDefaultValue() throws Exception {
-        nonStringTestHolder.testIntegerDefaultValue();
+    public void testInteger() throws Exception {
+        nonStringTestHolder.testInteger();
     }
 
     @Test
@@ -116,13 +111,23 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testLong() throws Exception {
-        nonStringTestHolder.testLong();
+    public void testIntegerDefaultValue() throws Exception {
+        nonStringTestHolder.testIntegerDefaultValue();
     }
 
     @Test
-    public void testLongDefaultValue() throws Exception {
-        nonStringTestHolder.testLongDefaultValue();
+    public void testIsEmpty() throws Exception {
+        nonStringTestHolder.testIsEmpty();
+    }
+
+    @Test
+    public void testListMissing() throws Exception {
+        nonStringTestHolder.testListMissing();
+    }
+
+    @Test
+    public void testLong() throws Exception {
+        nonStringTestHolder.testLong();
     }
 
     @Test
@@ -133,13 +138,13 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testShort() throws Exception {
-        nonStringTestHolder.testShort();
+    public void testLongDefaultValue() throws Exception {
+        nonStringTestHolder.testLongDefaultValue();
     }
 
     @Test
-    public void testShortDefaultValue() throws Exception {
-        nonStringTestHolder.testShortDefaultValue();
+    public void testShort() throws Exception {
+        nonStringTestHolder.testShort();
     }
 
     @Test
@@ -150,17 +155,12 @@ public abstract class BaseNonStringProperties {
     }
 
     @Test
-    public void testListMissing() throws Exception {
-        nonStringTestHolder.testListMissing();
+    public void testShortDefaultValue() throws Exception {
+        nonStringTestHolder.testShortDefaultValue();
     }
 
     @Test
     public void testSubset() throws Exception {
         nonStringTestHolder.testSubset();
     }
-
-    @Test
-    public void testIsEmpty() throws Exception {
-        nonStringTestHolder.testIsEmpty();
-    }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/ConfigurationAssert.java b/src/test/java/org/apache/commons/configuration2/ConfigurationAssert.java
index 31df2d7..16e6217 100644
--- a/src/test/java/org/apache/commons/configuration2/ConfigurationAssert.java
+++ b/src/test/java/org/apache/commons/configuration2/ConfigurationAssert.java
@@ -50,6 +50,18 @@ public class ConfigurationAssert {
     public static final File OUT_DIR = new File(OUT_DIR_NAME);
 
     /**
+     * Appends all keys in the specified configuration to the given collection.
+     *
+     * @param config the configuration
+     * @param collection the target collection
+     */
+    public static void appendKeys(final ImmutableConfiguration config, final Collection<String> collection) {
+        for (final Iterator<String> it = config.getKeys(); it.hasNext();) {
+            collection.add(it.next());
+        }
+    }
+
+    /**
      * Checks the content of a configuration.
      *
      * @param expected the expected properties
@@ -71,13 +83,21 @@ public class ConfigurationAssert {
     }
 
     /**
-     * Returns a {@code File} object for the specified test file.
+     * Helper method for testing the equals() implementation of a class. It is also checked, whether hashCode() is
+     * compatible with equals().
      *
-     * @param name the name of the test file
-     * @return a {@code File} object pointing to that test file
+     * @param o1 test object 1
+     * @param o2 test object 2
+     * @param expEquals the expected result of equals()
      */
-    public static File getTestFile(final String name) {
-        return new File(TEST_DIR, name);
+    public static void checkEquals(final Object o1, final Object o2, final boolean expEquals) {
+        assertEquals("Wrong result of equals()", expEquals, o1.equals(o2));
+        if (o2 != null) {
+            assertEquals("Not symmetric", expEquals, o2.equals(o1));
+        }
+        if (expEquals) {
+            assertEquals("Different hash codes", o1.hashCode(), o2.hashCode());
+        }
     }
 
     /**
@@ -91,41 +111,33 @@ public class ConfigurationAssert {
     }
 
     /**
-     * Returns a URL pointing to the specified test file. If the URL cannot be constructed, a runtime exception is thrown.
+     * Returns a URL pointing to the specified output file. If the URL cannot be constructed, a runtime exception is thrown.
      *
-     * @param name the name of the test file
+     * @param name the name of the output file
      * @return the corresponding URL
      */
-    public static URL getTestURL(final String name) {
-        return urlFromFile(getTestFile(name));
+    public static URL getOutURL(final String name) {
+        return urlFromFile(getOutFile(name));
     }
 
     /**
-     * Returns a URL pointing to the specified output file. If the URL cannot be constructed, a runtime exception is thrown.
+     * Returns a {@code File} object for the specified test file.
      *
-     * @param name the name of the output file
-     * @return the corresponding URL
+     * @param name the name of the test file
+     * @return a {@code File} object pointing to that test file
      */
-    public static URL getOutURL(final String name) {
-        return urlFromFile(getOutFile(name));
+    public static File getTestFile(final String name) {
+        return new File(TEST_DIR, name);
     }
 
     /**
-     * Helper method for testing the equals() implementation of a class. It is also checked, whether hashCode() is
-     * compatible with equals().
+     * Returns a URL pointing to the specified test file. If the URL cannot be constructed, a runtime exception is thrown.
      *
-     * @param o1 test object 1
-     * @param o2 test object 2
-     * @param expEquals the expected result of equals()
+     * @param name the name of the test file
+     * @return the corresponding URL
      */
-    public static void checkEquals(final Object o1, final Object o2, final boolean expEquals) {
-        assertEquals("Wrong result of equals()", expEquals, o1.equals(o2));
-        if (o2 != null) {
-            assertEquals("Not symmetric", expEquals, o2.equals(o1));
-        }
-        if (expEquals) {
-            assertEquals("Different hash codes", o1.hashCode(), o2.hashCode());
-        }
+    public static URL getTestURL(final String name) {
+        return urlFromFile(getTestFile(name));
     }
 
     /**
@@ -153,18 +165,6 @@ public class ConfigurationAssert {
     }
 
     /**
-     * Appends all keys in the specified configuration to the given collection.
-     *
-     * @param config the configuration
-     * @param collection the target collection
-     */
-    public static void appendKeys(final ImmutableConfiguration config, final Collection<String> collection) {
-        for (final Iterator<String> it = config.getKeys(); it.hasNext();) {
-            collection.add(it.next());
-        }
-    }
-
-    /**
      * Helper method for converting a file to a URL.
      *
      * @param file the file
diff --git a/src/test/java/org/apache/commons/configuration2/DatabaseConfigurationTestHelper.java b/src/test/java/org/apache/commons/configuration2/DatabaseConfigurationTestHelper.java
index 3d34822..e2f54de 100644
--- a/src/test/java/org/apache/commons/configuration2/DatabaseConfigurationTestHelper.java
+++ b/src/test/java/org/apache/commons/configuration2/DatabaseConfigurationTestHelper.java
@@ -42,18 +42,6 @@ import org.dbunit.operation.DatabaseOperation;
  *
  */
 public class DatabaseConfigurationTestHelper {
-    /** Constant for the JDBC driver class. */
-    public final String DATABASE_DRIVER = "org.hsqldb.jdbcDriver";
-
-    /** Constant for the connection URL. */
-    public final String DATABASE_URL = "jdbc:hsqldb:mem:testdb";
-
-    /** Constant for the DB user name. */
-    public final String DATABASE_USERNAME = "sa";
-
-    /** Constant for the DB password. */
-    public final String DATABASE_PASSWORD = "";
-
     /** Constant for the configuration table. */
     public static final String TABLE = "configuration";
 
@@ -72,6 +60,18 @@ public class DatabaseConfigurationTestHelper {
     /** Constant for the name of the test configuration. */
     public static final String CONFIG_NAME = "test";
 
+    /** Constant for the JDBC driver class. */
+    public final String DATABASE_DRIVER = "org.hsqldb.jdbcDriver";
+
+    /** Constant for the connection URL. */
+    public final String DATABASE_URL = "jdbc:hsqldb:mem:testdb";
+
+    /** Constant for the DB user name. */
+    public final String DATABASE_USERNAME = "sa";
+
+    /** Constant for the DB password. */
+    public final String DATABASE_PASSWORD = "";
+
     /** Stores the in-process database. */
     private HsqlDB hsqlDB;
 
@@ -84,6 +84,35 @@ public class DatabaseConfigurationTestHelper {
     private boolean autoCommit;
 
     /**
+     * Creates a configuration instance of the specified class with the given parameters.
+     *
+     * @param <T> the type of the result configuration
+     * @param configCls the configuration class
+     * @param params the parameters object
+     * @return the newly created configuration instance
+     * @throws ConfigurationException if an error occurs
+     */
+    public <T extends DatabaseConfiguration> T createConfig(final Class<T> configCls, final DatabaseBuilderParameters params) throws ConfigurationException {
+        return new BasicConfigurationBuilder<>(configCls).configure(params).getConfiguration();
+    }
+
+    /**
+     * Returns the {@code DataSource} managed by this class. The data source is created on first access.
+     *
+     * @return the {@code DataSource}
+     */
+    public DataSource getDatasource() {
+        if (datasource == null) {
+            try {
+                datasource = setUpDataSource();
+            } catch (final Exception ex) {
+                throw new ConfigurationRuntimeException("Could not create data source", ex);
+            }
+        }
+        return datasource;
+    }
+
+    /**
      * Returns the auto-commit mode of the configuration instances created by this helper.
      *
      * @return the auto-commit mode
@@ -113,72 +142,76 @@ public class DatabaseConfigurationTestHelper {
     }
 
     /**
-     * Frees the resources used by this helper class. This method can be called by a {@code tearDown()} method of a unit
-     * test class.
+     * Creates a database configuration with default settings.
      *
-     * @throws Exception if an error occurs
+     * @return the configuration
+     * @throws ConfigurationException if an error occurs
      */
-    public void tearDown() throws Exception {
-        if (datasource != null) {
-            datasource.getConnection().close();
-        }
-        hsqlDB.close();
+    public DatabaseConfiguration setUpConfig() throws ConfigurationException {
+        return setUpConfig(DatabaseConfiguration.class);
     }
 
     /**
-     * Returns a parameters object with default settings.
+     * Creates a database configuration with default settings of the specified class.
      *
-     * @return the parameters object
+     * @param <T> the type of the result configuration
+     * @param configCls the configuration class
+     * @return the newly created configuration instance
+     * @throws ConfigurationException if an error occurs
      */
-    public DatabaseBuilderParameters setUpDefaultParameters() {
-        return new Parameters().database().setDataSource(getDatasource()).setTable(TABLE).setKeyColumn(COL_KEY).setValueColumn(COL_VALUE)
-            .setAutoCommit(isAutoCommit());
+    public <T extends DatabaseConfiguration> T setUpConfig(final Class<T> configCls) throws ConfigurationException {
+        return createConfig(configCls, setUpDefaultParameters());
     }
 
     /**
-     * Returns a parameters object with settings for a configuration table containing the data of multiple configurations.
+     * Creates the internal data source. This method also initializes the database.
      *
-     * @param configName the name of the configuration instance or <b>null</b> for the default name
-     * @return the parameters object
+     * @return the data source
+     * @throws Exception if an error occurs
      */
-    public DatabaseBuilderParameters setUpMultiParameters(final String configName) {
-        return setUpDefaultParameters().setTable(TABLE_MULTI).setConfigurationNameColumn(COL_NAME)
-            .setConfigurationName(configName != null ? configName : CONFIG_NAME);
-    }
+    private DataSource setUpDataSource() throws Exception {
+        final BasicDataSource ds = new BasicDataSource();
+        ds.setDriverClassName(DATABASE_DRIVER);
+        ds.setUrl(DATABASE_URL);
+        ds.setUsername(DATABASE_USERNAME);
+        ds.setPassword(DATABASE_PASSWORD);
+        ds.setDefaultAutoCommit(!isAutoCommit());
 
-    /**
-     * Creates a configuration instance of the specified class with the given parameters.
-     *
-     * @param <T> the type of the result configuration
-     * @param configCls the configuration class
-     * @param params the parameters object
-     * @return the newly created configuration instance
-     * @throws ConfigurationException if an error occurs
-     */
-    public <T extends DatabaseConfiguration> T createConfig(final Class<T> configCls, final DatabaseBuilderParameters params) throws ConfigurationException {
-        return new BasicConfigurationBuilder<>(configCls).configure(params).getConfiguration();
+        // prepare the database
+        final Connection conn = ds.getConnection();
+        final IDatabaseConnection connection = new DatabaseConnection(conn);
+        final IDataSet dataSet = new XmlDataSet(new FileInputStream(ConfigurationAssert.getTestFile("dataset.xml")));
+
+        try {
+            DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
+        } finally {
+            if (!isAutoCommit()) {
+                conn.commit();
+            }
+            connection.close();
+        }
+
+        return ds;
     }
 
     /**
-     * Creates a database configuration with default settings of the specified class.
+     * Returns a parameters object with default settings.
      *
-     * @param <T> the type of the result configuration
-     * @param configCls the configuration class
-     * @return the newly created configuration instance
-     * @throws ConfigurationException if an error occurs
+     * @return the parameters object
      */
-    public <T extends DatabaseConfiguration> T setUpConfig(final Class<T> configCls) throws ConfigurationException {
-        return createConfig(configCls, setUpDefaultParameters());
+    public DatabaseBuilderParameters setUpDefaultParameters() {
+        return new Parameters().database().setDataSource(getDatasource()).setTable(TABLE).setKeyColumn(COL_KEY).setValueColumn(COL_VALUE)
+            .setAutoCommit(isAutoCommit());
     }
 
     /**
-     * Creates a database configuration with default settings.
+     * Creates a database configuration that supports multiple configurations in a table with default values.
      *
      * @return the configuration
      * @throws ConfigurationException if an error occurs
      */
-    public DatabaseConfiguration setUpConfig() throws ConfigurationException {
-        return setUpConfig(DatabaseConfiguration.class);
+    public DatabaseConfiguration setUpMultiConfig() throws ConfigurationException {
+        return setUpMultiConfig(DatabaseConfiguration.class, null);
     }
 
     /**
@@ -195,59 +228,26 @@ public class DatabaseConfigurationTestHelper {
     }
 
     /**
-     * Creates a database configuration that supports multiple configurations in a table with default values.
-     *
-     * @return the configuration
-     * @throws ConfigurationException if an error occurs
-     */
-    public DatabaseConfiguration setUpMultiConfig() throws ConfigurationException {
-        return setUpMultiConfig(DatabaseConfiguration.class, null);
-    }
-
-    /**
-     * Returns the {@code DataSource} managed by this class. The data source is created on first access.
+     * Returns a parameters object with settings for a configuration table containing the data of multiple configurations.
      *
-     * @return the {@code DataSource}
+     * @param configName the name of the configuration instance or <b>null</b> for the default name
+     * @return the parameters object
      */
-    public DataSource getDatasource() {
-        if (datasource == null) {
-            try {
-                datasource = setUpDataSource();
-            } catch (final Exception ex) {
-                throw new ConfigurationRuntimeException("Could not create data source", ex);
-            }
-        }
-        return datasource;
+    public DatabaseBuilderParameters setUpMultiParameters(final String configName) {
+        return setUpDefaultParameters().setTable(TABLE_MULTI).setConfigurationNameColumn(COL_NAME)
+            .setConfigurationName(configName != null ? configName : CONFIG_NAME);
     }
 
     /**
-     * Creates the internal data source. This method also initializes the database.
+     * Frees the resources used by this helper class. This method can be called by a {@code tearDown()} method of a unit
+     * test class.
      *
-     * @return the data source
      * @throws Exception if an error occurs
      */
-    private DataSource setUpDataSource() throws Exception {
-        final BasicDataSource ds = new BasicDataSource();
-        ds.setDriverClassName(DATABASE_DRIVER);
-        ds.setUrl(DATABASE_URL);
-        ds.setUsername(DATABASE_USERNAME);
-        ds.setPassword(DATABASE_PASSWORD);
-        ds.setDefaultAutoCommit(!isAutoCommit());
-
-        // prepare the database
-        final Connection conn = ds.getConnection();
-        final IDatabaseConnection connection = new DatabaseConnection(conn);
-        final IDataSet dataSet = new XmlDataSet(new FileInputStream(ConfigurationAssert.getTestFile("dataset.xml")));
-
-        try {
-            DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
-        } finally {
-            if (!isAutoCommit()) {
-                conn.commit();
-            }
-            connection.close();
+    public void tearDown() throws Exception {
+        if (datasource != null) {
+            datasource.getConnection().close();
         }
-
-        return ds;
+        hsqlDB.close();
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/Logging.java b/src/test/java/org/apache/commons/configuration2/Logging.java
index d71154a..7a864af 100644
--- a/src/test/java/org/apache/commons/configuration2/Logging.java
+++ b/src/test/java/org/apache/commons/configuration2/Logging.java
@@ -63,17 +63,17 @@ public class Logging extends Log4JLogger {
     }
 
     /**
-     * Base constructor.
+     * For use with a log4j factory.
      */
-    public Logging(final String name) {
-        super(name);
+    public Logging(final org.apache.log4j.Logger logger) {
+        super(logger);
     }
 
     /**
-     * For use with a log4j factory.
+     * Base constructor.
      */
-    public Logging(final org.apache.log4j.Logger logger) {
-        super(logger);
+    public Logging(final String name) {
+        super(name);
     }
 
     // ---------------------------------------------------------
@@ -94,143 +94,143 @@ public class Logging extends Log4JLogger {
     // ---------------------------------------------------------
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.TRACE}. When using a log4j version that does not support the
-     * {@code TRACE} level, the message will be logged at the {@code DEBUG} level.
+     * Logs a message with {@code org.apache.log4j.Priority.DEBUG}.
      *
      * @param message to log
-     * @see org.apache.commons.logging.Log#trace(Object)
+     * @see org.apache.commons.logging.Log#debug(Object)
      */
     @Override
-    public void trace(final Object message) {
-        getLogger().log(FQCN, traceLevel, message, null);
+    public void debug(final Object message) {
+        getLogger().log(FQCN, Level.DEBUG, message, null);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.TRACE}. When using a log4j version that does not support the
-     * {@code TRACE} level, the message will be logged at the {@code DEBUG} level.
+     * Logs a message with {@code org.apache.log4j.Priority.DEBUG}.
      *
      * @param message to log
      * @param t log this cause
-     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
      */
     @Override
-    public void trace(final Object message, final Throwable t) {
-        getLogger().log(FQCN, traceLevel, message, t);
+    public void debug(final Object message, final Throwable t) {
+        getLogger().log(FQCN, Level.DEBUG, message, t);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.DEBUG}.
+     * Logs a message with {@code org.apache.log4j.Priority.ERROR}.
      *
      * @param message to log
-     * @see org.apache.commons.logging.Log#debug(Object)
+     * @see org.apache.commons.logging.Log#error(Object)
      */
     @Override
-    public void debug(final Object message) {
-        getLogger().log(FQCN, Level.DEBUG, message, null);
+    public void error(final Object message) {
+        getLogger().log(FQCN, Level.ERROR, message, null);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.DEBUG}.
+     * Logs a message with {@code org.apache.log4j.Priority.ERROR}.
      *
      * @param message to log
      * @param t log this cause
-     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
      */
     @Override
-    public void debug(final Object message, final Throwable t) {
-        getLogger().log(FQCN, Level.DEBUG, message, t);
+    public void error(final Object message, final Throwable t) {
+        getLogger().log(FQCN, Level.ERROR, message, t);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.INFO}.
+     * Logs a message with {@code org.apache.log4j.Priority.FATAL}.
      *
      * @param message to log
-     * @see org.apache.commons.logging.Log#info(Object)
+     * @see org.apache.commons.logging.Log#fatal(Object)
      */
     @Override
-    public void info(final Object message) {
-        getLogger().log(FQCN, Level.INFO, message, null);
+    public void fatal(final Object message) {
+        getLogger().log(FQCN, Level.FATAL, message, null);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.INFO}.
+     * Logs a message with {@code org.apache.log4j.Priority.FATAL}.
      *
      * @param message to log
      * @param t log this cause
-     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
      */
     @Override
-    public void info(final Object message, final Throwable t) {
-        getLogger().log(FQCN, Level.INFO, message, t);
+    public void fatal(final Object message, final Throwable t) {
+        getLogger().log(FQCN, Level.FATAL, message, t);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.WARN}.
+     * Logs a message with {@code org.apache.log4j.Priority.INFO}.
      *
      * @param message to log
-     * @see org.apache.commons.logging.Log#warn(Object)
+     * @see org.apache.commons.logging.Log#info(Object)
      */
     @Override
-    public void warn(final Object message) {
-        getLogger().log(FQCN, Level.WARN, message, null);
+    public void info(final Object message) {
+        getLogger().log(FQCN, Level.INFO, message, null);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.WARN}.
+     * Logs a message with {@code org.apache.log4j.Priority.INFO}.
      *
      * @param message to log
      * @param t log this cause
-     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
      */
     @Override
-    public void warn(final Object message, final Throwable t) {
-        getLogger().log(FQCN, Level.WARN, message, t);
+    public void info(final Object message, final Throwable t) {
+        getLogger().log(FQCN, Level.INFO, message, t);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.ERROR}.
+     * Logs a message with {@code org.apache.log4j.Priority.TRACE}. When using a log4j version that does not support the
+     * {@code TRACE} level, the message will be logged at the {@code DEBUG} level.
      *
      * @param message to log
-     * @see org.apache.commons.logging.Log#error(Object)
+     * @see org.apache.commons.logging.Log#trace(Object)
      */
     @Override
-    public void error(final Object message) {
-        getLogger().log(FQCN, Level.ERROR, message, null);
+    public void trace(final Object message) {
+        getLogger().log(FQCN, traceLevel, message, null);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.ERROR}.
+     * Logs a message with {@code org.apache.log4j.Priority.TRACE}. When using a log4j version that does not support the
+     * {@code TRACE} level, the message will be logged at the {@code DEBUG} level.
      *
      * @param message to log
      * @param t log this cause
-     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
      */
     @Override
-    public void error(final Object message, final Throwable t) {
-        getLogger().log(FQCN, Level.ERROR, message, t);
+    public void trace(final Object message, final Throwable t) {
+        getLogger().log(FQCN, traceLevel, message, t);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.FATAL}.
+     * Logs a message with {@code org.apache.log4j.Priority.WARN}.
      *
      * @param message to log
-     * @see org.apache.commons.logging.Log#fatal(Object)
+     * @see org.apache.commons.logging.Log#warn(Object)
      */
     @Override
-    public void fatal(final Object message) {
-        getLogger().log(FQCN, Level.FATAL, message, null);
+    public void warn(final Object message) {
+        getLogger().log(FQCN, Level.WARN, message, null);
     }
 
     /**
-     * Logs a message with {@code org.apache.log4j.Priority.FATAL}.
+     * Logs a message with {@code org.apache.log4j.Priority.WARN}.
      *
      * @param message to log
      * @param t log this cause
-     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
      */
     @Override
-    public void fatal(final Object message, final Throwable t) {
-        getLogger().log(FQCN, Level.FATAL, message, t);
+    public void warn(final Object message, final Throwable t) {
+        getLogger().log(FQCN, Level.WARN, message, t);
     }
 
 }
diff --git a/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java b/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
index 46fe5e8..dffa865 100644
--- a/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
+++ b/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
@@ -64,38 +64,48 @@ public class MockInitialContextFactory implements InitialContextFactory {
     private static final String[] MISSING_NAMES = {"missing/list", "test/imaginarykey", "foo/bar"};
 
     /**
-     * Creates a {@code Context} object that is backed by a mock object. The mock context can be queried for the values of
-     * certain test properties. It also supports listing the contained (sub) properties.
+     * Adds a new name-and-value pair to an enum mock.
      *
-     * @param env the environment
-     * @return the context mock
+     * @param mockEnum the enum mock
+     * @param name the name
+     * @param value the value
      */
-    @Override
-    public Context getInitialContext(@SuppressWarnings("rawtypes") final Hashtable env) throws NamingException {
-        final boolean useCycles = env.containsKey(PROP_CYCLES);
+    private void addEnumPair(final Mock mockEnum, final String name, final Object value) {
+        final NameClassPair ncp = new NameClassPair(name, value.getClass().getName());
+        mockEnum.expectAndReturn("hasMore", true);
+        mockEnum.expectAndReturn("next", ncp);
+    }
 
-        final Mock mockTopCtx = createCtxMock(PREFIX);
-        final Mock mockCycleCtx = createCtxMock("");
-        final Mock mockPrfxCtx = createCtxMock("");
-        final Mock mockBaseCtx = new Mock(Context.class);
-        mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq(""), mockTopCtx.proxy());
-        mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx.proxy());
-        mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx.proxy());
-        mockPrfxCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(mockPrfxCtx, PROP_NAMES, PROP_VALUES).proxy());
+    /**
+     * Binds a property value to the mock context.
+     *
+     * @param mockCtx the context
+     * @param name the name of the property
+     * @param value the value of the property
+     */
+    private void bind(final Mock mockCtx, final String name, final String value) {
+        mockCtx.matchAndReturn(METHOD_LOOKUP, C.eq(name), value);
+        bindError(mockCtx, name + MISSING_PROP);
+    }
 
-        if (useCycles) {
-            mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycle"), mockCycleCtx.proxy());
-            mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""),
-                createEnumMock(mockTopCtx, new String[] {"test", "cycle"}, new Object[] {mockPrfxCtx.proxy(), mockCycleCtx.proxy()}).proxy());
-            final Mock mockEnum = createEnumMock(mockCycleCtx, PROP_NAMES, PROP_VALUES, false);
-            addEnumPair(mockEnum, "cycleCtx", mockCycleCtx.proxy());
-            closeEnum(mockEnum);
-            mockCycleCtx.matchAndReturn(METHOD_LIST, C.eq(""), mockEnum.proxy());
-            mockCycleCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycleCtx"), mockCycleCtx.proxy());
-        } else {
-            mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(mockTopCtx, new String[] {"test"}, new Object[] {mockPrfxCtx.proxy()}).proxy());
-        }
-        return (Context) mockBaseCtx.proxy();
+    /**
+     * Configures the mock to expect a call for a non existing property.
+     *
+     * @param mockCtx the mock
+     * @param name the name of the property
+     */
+    private void bindError(final Mock mockCtx, final String name) {
+        mockCtx.matchAndThrow(METHOD_LOOKUP, C.eq(name), new NameNotFoundException("unknown property"));
+    }
+
+    /**
+     * Closes an enumeration mock.
+     *
+     * @param mockEnum the mock
+     */
+    private void closeEnum(final Mock mockEnum) {
+        mockEnum.expectAndReturn("hasMore", false);
+        mockEnum.expect(METHOD_CLOSE);
     }
 
     /**
@@ -120,25 +130,16 @@ public class MockInitialContextFactory implements InitialContextFactory {
     }
 
     /**
-     * Binds a property value to the mock context.
-     *
-     * @param mockCtx the context
-     * @param name the name of the property
-     * @param value the value of the property
-     */
-    private void bind(final Mock mockCtx, final String name, final String value) {
-        mockCtx.matchAndReturn(METHOD_LOOKUP, C.eq(name), value);
-        bindError(mockCtx, name + MISSING_PROP);
-    }
-
-    /**
-     * Configures the mock to expect a call for a non existing property.
+     * Creates and initializes a mock for a naming enumeration that expects to be closed. This is a shortcut of
+     * createEnumMock(mockCtx, names, values, true);
      *
-     * @param mockCtx the mock
-     * @param name the name of the property
+     * @param mockCtx the mock representing the context
+     * @param names the names contained in the iteration
+     * @param values the corresponding values
+     * @return the mock for the enumeration
      */
-    private void bindError(final Mock mockCtx, final String name) {
-        mockCtx.matchAndThrow(METHOD_LOOKUP, C.eq(name), new NameNotFoundException("unknown property"));
+    private Mock createEnumMock(final Mock mockCtx, final String[] names, final Object[] values) {
+        return createEnumMock(mockCtx, names, values, true);
     }
 
     /**
@@ -162,38 +163,37 @@ public class MockInitialContextFactory implements InitialContextFactory {
     }
 
     /**
-     * Creates and initializes a mock for a naming enumeration that expects to be closed. This is a shortcut of
-     * createEnumMock(mockCtx, names, values, true);
+     * Creates a {@code Context} object that is backed by a mock object. The mock context can be queried for the values of
+     * certain test properties. It also supports listing the contained (sub) properties.
      *
-     * @param mockCtx the mock representing the context
-     * @param names the names contained in the iteration
-     * @param values the corresponding values
-     * @return the mock for the enumeration
+     * @param env the environment
+     * @return the context mock
      */
-    private Mock createEnumMock(final Mock mockCtx, final String[] names, final Object[] values) {
-        return createEnumMock(mockCtx, names, values, true);
-    }
+    @Override
+    public Context getInitialContext(@SuppressWarnings("rawtypes") final Hashtable env) throws NamingException {
+        final boolean useCycles = env.containsKey(PROP_CYCLES);
 
-    /**
-     * Adds a new name-and-value pair to an enum mock.
-     *
-     * @param mockEnum the enum mock
-     * @param name the name
-     * @param value the value
-     */
-    private void addEnumPair(final Mock mockEnum, final String name, final Object value) {
-        final NameClassPair ncp = new NameClassPair(name, value.getClass().getName());
-        mockEnum.expectAndReturn("hasMore", true);
-        mockEnum.expectAndReturn("next", ncp);
-    }
+        final Mock mockTopCtx = createCtxMock(PREFIX);
+        final Mock mockCycleCtx = createCtxMock("");
+        final Mock mockPrfxCtx = createCtxMock("");
+        final Mock mockBaseCtx = new Mock(Context.class);
+        mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq(""), mockTopCtx.proxy());
+        mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx.proxy());
+        mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx.proxy());
+        mockPrfxCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(mockPrfxCtx, PROP_NAMES, PROP_VALUES).proxy());
 
-    /**
-     * Closes an enumeration mock.
-     *
-     * @param mockEnum the mock
-     */
-    private void closeEnum(final Mock mockEnum) {
-        mockEnum.expectAndReturn("hasMore", false);
-        mockEnum.expect(METHOD_CLOSE);
+        if (useCycles) {
+            mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycle"), mockCycleCtx.proxy());
+            mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""),
+                createEnumMock(mockTopCtx, new String[] {"test", "cycle"}, new Object[] {mockPrfxCtx.proxy(), mockCycleCtx.proxy()}).proxy());
+            final Mock mockEnum = createEnumMock(mockCycleCtx, PROP_NAMES, PROP_VALUES, false);
+            addEnumPair(mockEnum, "cycleCtx", mockCycleCtx.proxy());
+            closeEnum(mockEnum);
+            mockCycleCtx.matchAndReturn(METHOD_LIST, C.eq(""), mockEnum.proxy());
+            mockCycleCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycleCtx"), mockCycleCtx.proxy());
+        } else {
+            mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(mockTopCtx, new String[] {"test"}, new Object[] {mockPrfxCtx.proxy()}).proxy());
+        }
+        return (Context) mockBaseCtx.proxy();
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java b/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java
index b1c6fd6..05840d9 100644
--- a/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java
@@ -31,12 +31,8 @@ public class NonCloneableConfiguration extends AbstractConfiguration {
     protected void addPropertyDirect(final String key, final Object value) {
     }
 
-    /**
-     * Dummy implementation of this method.
-     */
     @Override
-    protected boolean isEmptyInternal() {
-        return true;
+    protected void clearPropertyDirect(final String key) {
     }
 
     /**
@@ -63,7 +59,11 @@ public class NonCloneableConfiguration extends AbstractConfiguration {
         return null;
     }
 
+    /**
+     * Dummy implementation of this method.
+     */
     @Override
-    protected void clearPropertyDirect(final String key) {
+    protected boolean isEmptyInternal() {
+        return true;
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/NonStringTestHolder.java b/src/test/java/org/apache/commons/configuration2/NonStringTestHolder.java
index 52ce5e2..45bc9cc 100644
--- a/src/test/java/org/apache/commons/configuration2/NonStringTestHolder.java
+++ b/src/test/java/org/apache/commons/configuration2/NonStringTestHolder.java
@@ -92,6 +92,15 @@ public class NonStringTestHolder {
         Assert.assertEquals(10, intValue);
     }
 
+    public void testIsEmpty() throws Exception {
+        Assert.assertTrue("Configuration should not be empty", !configuration.isEmpty());
+    }
+
+    public void testListMissing() throws Exception {
+        final List<?> list = configuration.getList("missing.list");
+        Assert.assertTrue("'missing.list' is not empty", list.isEmpty());
+    }
+
     public void testLong() throws Exception {
         final long longValue = configuration.getLong("test.long");
         Assert.assertEquals(1000000, longValue);
@@ -114,11 +123,6 @@ public class NonStringTestHolder {
         Assert.assertEquals(1, shortValue);
     }
 
-    public void testListMissing() throws Exception {
-        final List<?> list = configuration.getList("missing.list");
-        Assert.assertTrue("'missing.list' is not empty", list.isEmpty());
-    }
-
     public void testSubset() throws Exception {
         final Configuration subset = configuration.subset("test");
 
@@ -133,8 +137,4 @@ public class NonStringTestHolder {
         Assert.assertTrue("'short' key not found in the subset key iterator", foundKeyValue);
     }
 
-    public void testIsEmpty() throws Exception {
-        Assert.assertTrue("Configuration should not be empty", !configuration.isEmpty());
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/configuration2/SynchronizerTestImpl.java b/src/test/java/org/apache/commons/configuration2/SynchronizerTestImpl.java
index 123ebfc..404d3c9 100644
--- a/src/test/java/org/apache/commons/configuration2/SynchronizerTestImpl.java
+++ b/src/test/java/org/apache/commons/configuration2/SynchronizerTestImpl.java
@@ -26,10 +26,26 @@ import org.apache.commons.configuration2.sync.Synchronizer;
  *
  */
 public class SynchronizerTestImpl implements Synchronizer {
+    /**
+     * An enumeration with the methods of the Synchronizer which can be called.
+     */
+    public enum Methods {
+        BEGIN_READ, END_READ, BEGIN_WRITE, END_WRITE
+    }
+
     /** A buffer for registering the methods invoked by clients. */
     private final StringBuilder methods = new StringBuilder();
 
     /**
+     * Adds a method name to the internal buffer. Called by all interface methods.
+     *
+     * @param m the method that was invoked
+     */
+    private void append(final Methods m) {
+        methods.append(m);
+    }
+
+    /**
      * {@inheritDoc} Registers this invocation.
      */
     @Override
@@ -41,16 +57,37 @@ public class SynchronizerTestImpl implements Synchronizer {
      * {@inheritDoc} Registers this invocation.
      */
     @Override
-    public void endRead() {
-        append(Methods.END_READ);
+    public void beginWrite() {
+        append(Methods.BEGIN_WRITE);
+    }
+
+    /**
+     * Clears the methods recorded so far.
+     */
+    public void clear() {
+        methods.setLength(0);
+    }
+
+    /**
+     * Generates a string with expected methods from the given array.
+     *
+     * @param expMethods the array with expected methods
+     * @return a corresponding string representation
+     */
+    private String constructExpectedMethods(final Methods... expMethods) {
+        final StringBuilder buf = new StringBuilder();
+        for (final Methods m : expMethods) {
+            buf.append(m);
+        }
+        return buf.toString();
     }
 
     /**
      * {@inheritDoc} Registers this invocation.
      */
     @Override
-    public void beginWrite() {
-        append(Methods.BEGIN_WRITE);
+    public void endRead() {
+        append(Methods.END_READ);
     }
 
     /**
@@ -71,12 +108,12 @@ public class SynchronizerTestImpl implements Synchronizer {
     }
 
     /**
-     * Verifies that the specified methods were called at the beginning of the interaction with the synchronizer.
+     * Verifies that the specified sequence of methods was called somewhere in the interaction with the synchronizer.
      *
      * @param expMethods the expected methods
      */
-    public void verifyStart(final Methods... expMethods) {
-        assertTrue("Wrong methods at start: " + methods, methods.toString().startsWith(constructExpectedMethods(expMethods)));
+    public void verifyContains(final Methods... expMethods) {
+        assertTrue("Expected methods not found: " + methods, methods.toString().indexOf(constructExpectedMethods(expMethods)) >= 0);
     }
 
     /**
@@ -89,48 +126,11 @@ public class SynchronizerTestImpl implements Synchronizer {
     }
 
     /**
-     * Verifies that the specified sequence of methods was called somewhere in the interaction with the synchronizer.
+     * Verifies that the specified methods were called at the beginning of the interaction with the synchronizer.
      *
      * @param expMethods the expected methods
      */
-    public void verifyContains(final Methods... expMethods) {
-        assertTrue("Expected methods not found: " + methods, methods.toString().indexOf(constructExpectedMethods(expMethods)) >= 0);
-    }
-
-    /**
-     * Clears the methods recorded so far.
-     */
-    public void clear() {
-        methods.setLength(0);
-    }
-
-    /**
-     * Generates a string with expected methods from the given array.
-     *
-     * @param expMethods the array with expected methods
-     * @return a corresponding string representation
-     */
-    private String constructExpectedMethods(final Methods... expMethods) {
-        final StringBuilder buf = new StringBuilder();
-        for (final Methods m : expMethods) {
-            buf.append(m);
-        }
-        return buf.toString();
-    }
-
-    /**
-     * Adds a method name to the internal buffer. Called by all interface methods.
-     *
-     * @param m the method that was invoked
-     */
-    private void append(final Methods m) {
-        methods.append(m);
-    }
-
-    /**
-     * An enumeration with the methods of the Synchronizer which can be called.
-     */
-    public enum Methods {
-        BEGIN_READ, END_READ, BEGIN_WRITE, END_WRITE
+    public void verifyStart(final Methods... expMethods) {
+        assertTrue("Wrong methods at start: " + methods, methods.toString().startsWith(constructExpectedMethods(expMethods)));
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestAbstractConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestAbstractConfiguration.java
index 1f4a507..a788d6a 100644
--- a/src/test/java/org/apache/commons/configuration2/TestAbstractConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestAbstractConfiguration.java
@@ -58,33 +58,6 @@ public abstract class TestAbstractConfiguration {
     protected abstract AbstractConfiguration getEmptyConfiguration();
 
     @Test
-    public void testGetProperty() {
-        final Configuration config = getConfiguration();
-        assertEquals("key1", "value1", config.getProperty("key1"));
-        assertEquals("key2", "value2", config.getProperty("key2"));
-        assertNull("key3", config.getProperty("key3"));
-    }
-
-    @Test
-    public void testList() {
-        final Configuration config = getConfiguration();
-
-        final List<?> list = config.getList("list");
-        assertNotNull("list not found", config.getProperty("list"));
-        assertEquals("list size", 2, list.size());
-        assertTrue("'value1' is not in the list", list.contains("value1"));
-        assertTrue("'value2' is not in the list", list.contains("value2"));
-    }
-
-    /**
-     * Tests whether the escape character for list delimiters is recocknized and removed.
-     */
-    @Test
-    public void testListEscaped() {
-        assertEquals("Wrong value for escaped list", "value1,value2", getConfiguration().getString("listesc"));
-    }
-
-    @Test
     public void testAddPropertyDirect() {
         final AbstractConfiguration config = getConfiguration();
         config.addPropertyDirect("key3", "value3");
@@ -104,20 +77,10 @@ public abstract class TestAbstractConfiguration {
     }
 
     @Test
-    public void testIsEmpty() {
+    public void testClearProperty() {
         final Configuration config = getConfiguration();
-        assertFalse("the configuration is empty", config.isEmpty());
-        assertTrue("the configuration is not empty", getEmptyConfiguration().isEmpty());
-    }
-
-    @Test
-    public void testSize() {
-        assertEquals("Wrong size", 4, getConfiguration().size());
-    }
-
-    @Test
-    public void testSizeEmpty() {
-        assertEquals("Wrong size of empty configuration", 0, getEmptyConfiguration().size());
+        config.clearProperty("key2");
+        assertFalse("key2 not cleared", config.containsKey("key2"));
     }
 
     @Test
@@ -127,11 +90,20 @@ public abstract class TestAbstractConfiguration {
         assertFalse("key3 found", config.containsKey("key3"));
     }
 
+    /**
+     * Tests the exception message triggered by the conversion to BigInteger. This test is related to CONFIGURATION-357.
+     */
     @Test
-    public void testClearProperty() {
+    public void testGetBigIntegerConversion() {
         final Configuration config = getConfiguration();
-        config.clearProperty("key2");
-        assertFalse("key2 not cleared", config.containsKey("key2"));
+        try {
+            config.getBigInteger("key1");
+            fail("No conversion exception thrown!");
+        } catch (final ConversionException cex) {
+            assertTrue("Key not found in exception message: " + cex, cex.getMessage().contains("'key1'"));
+            assertTrue("Target class not found in exception message: " + cex, cex.getMessage().contains(BigInteger.class.getName()));
+            assertTrue("Value not found in exception message: " + cex, cex.getMessage().contains(config.getString("key1")));
+        }
     }
 
     @Test
@@ -156,6 +128,40 @@ public abstract class TestAbstractConfiguration {
         ListAssert.assertEquals("keys", expectedKeys, actualKeys);
     }
 
+    @Test
+    public void testGetProperty() {
+        final Configuration config = getConfiguration();
+        assertEquals("key1", "value1", config.getProperty("key1"));
+        assertEquals("key2", "value2", config.getProperty("key2"));
+        assertNull("key3", config.getProperty("key3"));
+    }
+
+    @Test
+    public void testIsEmpty() {
+        final Configuration config = getConfiguration();
+        assertFalse("the configuration is empty", config.isEmpty());
+        assertTrue("the configuration is not empty", getEmptyConfiguration().isEmpty());
+    }
+
+    @Test
+    public void testList() {
+        final Configuration config = getConfiguration();
+
+        final List<?> list = config.getList("list");
+        assertNotNull("list not found", config.getProperty("list"));
+        assertEquals("list size", 2, list.size());
+        assertTrue("'value1' is not in the list", list.contains("value1"));
+        assertTrue("'value2' is not in the list", list.contains("value2"));
+    }
+
+    /**
+     * Tests whether the escape character for list delimiters is recocknized and removed.
+     */
+    @Test
+    public void testListEscaped() {
+        assertEquals("Wrong value for escaped list", "value1,value2", getConfiguration().getString("listesc"));
+    }
+
     /**
      * Tests accessing the configuration's logger.
      */
@@ -168,19 +174,13 @@ public abstract class TestAbstractConfiguration {
         assertSame("Logger was not set", log, config.getLogger());
     }
 
-    /**
-     * Tests the exception message triggered by the conversion to BigInteger. This test is related to CONFIGURATION-357.
-     */
     @Test
-    public void testGetBigIntegerConversion() {
-        final Configuration config = getConfiguration();
-        try {
-            config.getBigInteger("key1");
-            fail("No conversion exception thrown!");
-        } catch (final ConversionException cex) {
-            assertTrue("Key not found in exception message: " + cex, cex.getMessage().contains("'key1'"));
-            assertTrue("Target class not found in exception message: " + cex, cex.getMessage().contains(BigInteger.class.getName()));
-            assertTrue("Value not found in exception message: " + cex, cex.getMessage().contains(config.getString("key1")));
-        }
+    public void testSize() {
+        assertEquals("Wrong size", 4, getConfiguration().size());
+    }
+
+    @Test
+    public void testSizeEmpty() {
+        assertEquals("Wrong size of empty configuration", 0, getEmptyConfiguration().size());
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationBasicFeatures.java b/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationBasicFeatures.java
index 04c7846..67d051a 100644
--- a/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationBasicFeatures.java
+++ b/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationBasicFeatures.java
@@ -50,65 +50,89 @@ import org.junit.Test;
  *
  */
 public class TestAbstractConfigurationBasicFeatures {
-    /** Constant for text to be used in tests for variable substitution. */
-    private static final String SUBST_TXT = "The ${animal} jumps over the ${target}.";
-
-    /** Constant for the prefix of test keys. */
-    private static final String KEY_PREFIX = "key";
-
-    /** Constant for the number of properties in tests for copy operations. */
-    private static final int PROP_COUNT = 12;
-
     /**
-     * Tests the clear() implementation of AbstractConfiguration if the iterator returned by getKeys() does not support the
-     * remove() operation.
+     * An event listener implementation that simply collects all received configuration events.
      */
-    @Test
-    public void testClearIteratorNoRemove() {
-        final AbstractConfiguration config = new TestConfigurationImpl(new BaseConfiguration()) {
-            // return an iterator that does not support remove operations
-            @Override
-            protected Iterator<String> getKeysInternal() {
-                final Collection<String> keyCol = new ArrayList<>();
-                ConfigurationAssert.appendKeys(getUnderlyingConfiguration(), keyCol);
-                final String[] keys = keyCol.toArray(new String[keyCol.size()]);
-                return Arrays.asList(keys).iterator();
-            }
-        };
-        for (int i = 0; i < 20; i++) {
-            config.addProperty("key" + i, "value" + i);
+    private static class CollectingConfigurationListener implements EventListener<ConfigurationEvent> {
+        final List<ConfigurationEvent> events = new ArrayList<>();
+
+        @Override
+        public void onEvent(final ConfigurationEvent event) {
+            events.add(event);
         }
-        config.clear();
-        assertTrue("Configuration not empty", config.isEmpty());
     }
 
     /**
-     * Tests escaping the variable marker, so that no interpolation will be performed.
+     * A test configuration implementation. This implementation inherits directly from AbstractConfiguration. For
+     * implementing the required functionality another implementation of AbstractConfiguration is used; all methods that
+     * need to be implemented delegate to this wrapped configuration.
      */
-    @Test
-    public void testInterpolateEscape() {
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        config.addProperty("mypath", "$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar\\,$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar");
-        assertEquals("Wrong interpolated value", "${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar,${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar",
-            config.getString("mypath"));
-    }
+    static class TestConfigurationImpl extends AbstractConfiguration {
+        /** Stores the underlying configuration. */
+        private final AbstractConfiguration config;
 
-    /**
-     * Tests adding list properties. The single elements of the list should be added.
-     */
-    @Test
-    public void testAddPropertyList() {
-        checkAddListProperty(new TestConfigurationImpl(new PropertiesConfiguration()));
+        public TestConfigurationImpl(final AbstractConfiguration wrappedConfig) {
+            config = wrappedConfig;
+        }
+
+        @Override
+        protected void addPropertyDirect(final String key, final Object value) {
+            config.addPropertyDirect(key, value);
+        }
+
+        @Override
+        protected void clearPropertyDirect(final String key) {
+            config.clearPropertyDirect(key);
+        }
+
+        @Override
+        protected boolean containsKeyInternal(final String key) {
+            return config.containsKey(key);
+        }
+
+        @Override
+        protected Iterator<String> getKeysInternal() {
+            return config.getKeys();
+        }
+
+        @Override
+        protected Object getPropertyInternal(final String key) {
+            return config.getProperty(key);
+        }
+
+        public AbstractConfiguration getUnderlyingConfiguration() {
+            return config;
+        }
+
+        @Override
+        protected boolean isEmptyInternal() {
+            return config.isEmpty();
+        }
     }
 
+    /** Constant for text to be used in tests for variable substitution. */
+    private static final String SUBST_TXT = "The ${animal} jumps over the ${target}.";
+
+    /** Constant for the prefix of test keys. */
+    private static final String KEY_PREFIX = "key";
+
+    /** Constant for the number of properties in tests for copy operations. */
+    private static final int PROP_COUNT = 12;
+
     /**
-     * Tests adding list properties if delimiter parsing is disabled.
+     * Prepares a test configuration for a test for a list conversion. The configuration is populated with a list property.
+     * The returned list contains the expected list values converted to integers.
+     *
+     * @param config the test configuration
+     * @return the list with expected values
      */
-    @Test
-    public void testAddPropertyListNoDelimiterParsing() {
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        checkAddListProperty(config);
+    private static List<Integer> prepareListTest(final PropertiesConfiguration config) {
+        final List<Integer> expected = new ArrayList<>(PROP_COUNT);
+        for (int i = 0; i < PROP_COUNT; i++) {
+            config.addProperty(KEY_PREFIX, String.valueOf(i));
+            expected.add(i);
+        }
+        return expected;
     }
 
     /**
@@ -130,68 +154,117 @@ public class TestAbstractConfigurationBasicFeatures {
     }
 
     /**
-     * Tests the copy() method.
+     * Tests whether the correct events are received for a copy operation.
+     *
+     * @param l the event listener
+     * @param src the configuration that was copied
+     * @param eventType the expected event type
      */
-    @Test
-    public void testCopy() {
-        final AbstractConfiguration config = setUpDestConfig();
-        final Configuration srcConfig = setUpSourceConfig();
-        config.copy(srcConfig);
-        for (int i = 0; i < PROP_COUNT; i++) {
-            final String key = KEY_PREFIX + i;
-            if (srcConfig.containsKey(key)) {
-                assertEquals("Value not replaced: " + key, srcConfig.getProperty(key), config.getProperty(key));
+    private void checkCopyEvents(final CollectingConfigurationListener l, final Configuration src, final EventType<?> eventType) {
+        final Map<String, ConfigurationEvent> events = new HashMap<>();
+        for (final ConfigurationEvent e : l.events) {
+            assertEquals("Wrong event type", eventType, e.getEventType());
+            assertTrue("Unknown property: " + e.getPropertyName(), src.containsKey(e.getPropertyName()));
+            if (!e.isBeforeUpdate()) {
+                assertTrue("After event without before event", events.containsKey(e.getPropertyName()));
             } else {
-                assertEquals("Value modified: " + key, "value" + i, config.getProperty(key));
+                events.put(e.getPropertyName(), e);
             }
         }
+
+        for (final Iterator<String> it = src.getKeys(); it.hasNext();) {
+            final String key = it.next();
+            assertTrue("No event received for key " + key, events.containsKey(key));
+        }
     }
 
     /**
-     * Tests the copy() method if properties with multiple values and escaped list delimiters are involved.
+     * Helper method for checking getList() if the property value is a scalar.
+     *
+     * @param value the value of the property
      */
-    @Test
-    public void testCopyWithLists() {
-        final Configuration srcConfig = setUpSourceConfig();
-        final AbstractConfiguration config = setUpDestConfig();
-        config.copy(srcConfig);
-        checkListProperties(config);
+    private void checkGetListScalar(final Object value) {
+        final BaseConfiguration config = new BaseConfiguration();
+        config.addProperty(KEY_PREFIX, value);
+        final List<Object> lst = config.getList(KEY_PREFIX);
+        assertEquals("Wrong number of values", 1, lst.size());
+        assertEquals("Wrong value", value.toString(), lst.get(0));
     }
 
     /**
-     * Tests the events generated by a copy() operation.
+     * Helper method for checking getStringArray() if the property value is a scalar.
+     *
+     * @param value the value of the property
      */
-    @Test
-    public void testCopyEvents() {
-        final AbstractConfiguration config = setUpDestConfig();
-        final Configuration srcConfig = setUpSourceConfig();
-        final CollectingConfigurationListener l = new CollectingConfigurationListener();
-        config.addEventListener(ConfigurationEvent.ANY, l);
-        config.copy(srcConfig);
-        checkCopyEvents(l, srcConfig, ConfigurationEvent.SET_PROPERTY);
+    private void checkGetStringArrayScalar(final Object value) {
+        final BaseConfiguration config = new BaseConfiguration();
+        config.addProperty(KEY_PREFIX, value);
+        final String[] array = config.getStringArray(KEY_PREFIX);
+        assertEquals("Weong number of elements", 1, array.length);
+        assertEquals("Wrong value", value.toString(), array[0]);
     }
 
     /**
-     * Tests copying a null configuration. This should be a noop.
+     * Tests the values of list properties after a copy operation.
+     *
+     * @param config the configuration to test
+     */
+    private void checkListProperties(final Configuration config) {
+        List<Object> values = config.getList("list1");
+        assertEquals("Wrong number of elements in list 1", 3, values.size());
+        values = config.getList("list2");
+        assertEquals("Wrong number of elements in list 2", 2, values.size());
+        assertEquals("Wrong value 1", "3,1415", values.get(0));
+        assertEquals("Wrong value 2", "9,81", values.get(1));
+    }
+
+    /**
+     * Creates the destination configuration for testing the copy() and append() methods. This configuration contains keys
+     * with a running index and corresponding values starting with the prefix "value".
+     *
+     * @return the destination configuration for copy operations
+     */
+    private AbstractConfiguration setUpDestConfig() {
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        for (int i = 0; i < PROP_COUNT; i++) {
+            config.addProperty(KEY_PREFIX + i, "value" + i);
+        }
+        return config;
+    }
+
+    /**
+     * Creates the source configuration for testing the copy() and append() methods. This configuration contains keys with
+     * an odd index and values starting with the prefix "src". There are also some list properties.
+     *
+     * @return the source configuration for copy operations
+     */
+    private Configuration setUpSourceConfig() {
+        final BaseConfiguration config = new BaseConfiguration();
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        for (int i = 1; i < PROP_COUNT; i += 2) {
+            config.addProperty(KEY_PREFIX + i, "src" + i);
+        }
+        config.addProperty("list1", "1,2,3");
+        config.addProperty("list2", "3\\,1415,9\\,81");
+        return config;
+    }
+
+    /**
+     * Tests adding list properties. The single elements of the list should be added.
      */
     @Test
-    public void testCopyNull() {
-        final AbstractConfiguration config = setUpDestConfig();
-        config.copy(null);
-        ConfigurationAssert.assertConfigurationEquals(setUpDestConfig(), config);
+    public void testAddPropertyList() {
+        checkAddListProperty(new TestConfigurationImpl(new PropertiesConfiguration()));
     }
 
     /**
-     * Tests whether list delimiters are correctly handled when copying a configuration.
+     * Tests adding list properties if delimiter parsing is disabled.
      */
     @Test
-    public void testCopyDelimiterHandling() {
-        final BaseConfiguration srcConfig = new BaseConfiguration();
-        final BaseConfiguration dstConfig = new BaseConfiguration();
-        dstConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        srcConfig.setProperty(KEY_PREFIX, "C:\\Temp\\,D:\\Data");
-        dstConfig.copy(srcConfig);
-        assertEquals("Wrong property value", srcConfig.getString(KEY_PREFIX), dstConfig.getString(KEY_PREFIX));
+    public void testAddPropertyListNoDelimiterParsing() {
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        checkAddListProperty(config);
     }
 
     /**
@@ -216,16 +289,19 @@ public class TestAbstractConfigurationBasicFeatures {
     }
 
     /**
-     * Tests the append() method when properties with multiple values and escaped list delimiters are involved.
+     * Tests whether the list delimiter is correctly handled if a configuration is appended.
      */
     @Test
-    public void testAppendWithLists() {
-        final AbstractConfiguration config = setUpDestConfig();
-        config.append(setUpSourceConfig());
-        checkListProperties(config);
-    }
-
-    /**
+    public void testAppendDelimiterHandling() {
+        final BaseConfiguration srcConfig = new BaseConfiguration();
+        final BaseConfiguration dstConfig = new BaseConfiguration();
+        dstConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        srcConfig.setProperty(KEY_PREFIX, "C:\\Temp\\,D:\\Data");
+        dstConfig.append(srcConfig);
+        assertEquals("Wrong property value", srcConfig.getString(KEY_PREFIX), dstConfig.getString(KEY_PREFIX));
+    }
+
+    /**
      * Tests the events generated by an append() operation.
      */
     @Test
@@ -249,366 +325,408 @@ public class TestAbstractConfigurationBasicFeatures {
     }
 
     /**
-     * Tests whether the list delimiter is correctly handled if a configuration is appended.
+     * Tests the append() method when properties with multiple values and escaped list delimiters are involved.
      */
     @Test
-    public void testAppendDelimiterHandling() {
-        final BaseConfiguration srcConfig = new BaseConfiguration();
-        final BaseConfiguration dstConfig = new BaseConfiguration();
-        dstConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        srcConfig.setProperty(KEY_PREFIX, "C:\\Temp\\,D:\\Data");
-        dstConfig.append(srcConfig);
-        assertEquals("Wrong property value", srcConfig.getString(KEY_PREFIX), dstConfig.getString(KEY_PREFIX));
+    public void testAppendWithLists() {
+        final AbstractConfiguration config = setUpDestConfig();
+        config.append(setUpSourceConfig());
+        checkListProperties(config);
     }
 
     /**
-     * Tests whether environment variables can be interpolated.
+     * Tests the clear() implementation of AbstractConfiguration if the iterator returned by getKeys() does not support the
+     * remove() operation.
      */
     @Test
-    public void testInterpolateEnvironmentVariables() {
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        InterpolationTestHelper.testInterpolationEnvironment(config);
+    public void testClearIteratorNoRemove() {
+        final AbstractConfiguration config = new TestConfigurationImpl(new BaseConfiguration()) {
+            // return an iterator that does not support remove operations
+            @Override
+            protected Iterator<String> getKeysInternal() {
+                final Collection<String> keyCol = new ArrayList<>();
+                ConfigurationAssert.appendKeys(getUnderlyingConfiguration(), keyCol);
+                final String[] keys = keyCol.toArray(new String[keyCol.size()]);
+                return Arrays.asList(keys).iterator();
+            }
+        };
+        for (int i = 0; i < 20; i++) {
+            config.addProperty("key" + i, "value" + i);
+        }
+        config.clear();
+        assertTrue("Configuration not empty", config.isEmpty());
     }
 
     /**
-     * Tests whether prefix lookups can be added to an existing {@code ConfigurationInterpolator}.
+     * Tests the copy() method.
      */
     @Test
-    public void testSetPrefixLookupsExistingInterpolator() {
-        final Lookup look = EasyMock.createMock(Lookup.class);
-        EasyMock.replay(look);
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        final int count = config.getInterpolator().getLookups().size();
-        final Map<String, Lookup> lookups = new HashMap<>();
-        lookups.put("test", look);
-        config.setPrefixLookups(lookups);
-        final Map<String, Lookup> lookups2 = config.getInterpolator().getLookups();
-        assertEquals("Not added", count + 1, lookups2.size());
-        assertSame("Not found", look, lookups2.get("test"));
+    public void testCopy() {
+        final AbstractConfiguration config = setUpDestConfig();
+        final Configuration srcConfig = setUpSourceConfig();
+        config.copy(srcConfig);
+        for (int i = 0; i < PROP_COUNT; i++) {
+            final String key = KEY_PREFIX + i;
+            if (srcConfig.containsKey(key)) {
+                assertEquals("Value not replaced: " + key, srcConfig.getProperty(key), config.getProperty(key));
+            } else {
+                assertEquals("Value modified: " + key, "value" + i, config.getProperty(key));
+            }
+        }
     }
 
     /**
-     * Tests whether prefix lookups can be added if no {@code ConfigurationInterpolator} exists yet.
+     * Tests whether list delimiters are correctly handled when copying a configuration.
      */
     @Test
-    public void testSetPrefixLookupsNoInterpolator() {
-        final Lookup look = EasyMock.createMock(Lookup.class);
-        EasyMock.replay(look);
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.setInterpolator(null);
-        config.setPrefixLookups(Collections.singletonMap("test", look));
-        final Map<String, Lookup> lookups = config.getInterpolator().getLookups();
-        assertEquals("Wrong number of lookups", 1, lookups.size());
-        assertSame("Not found", look, lookups.get("test"));
+    public void testCopyDelimiterHandling() {
+        final BaseConfiguration srcConfig = new BaseConfiguration();
+        final BaseConfiguration dstConfig = new BaseConfiguration();
+        dstConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        srcConfig.setProperty(KEY_PREFIX, "C:\\Temp\\,D:\\Data");
+        dstConfig.copy(srcConfig);
+        assertEquals("Wrong property value", srcConfig.getString(KEY_PREFIX), dstConfig.getString(KEY_PREFIX));
     }
 
     /**
-     * Tests whether default lookups can be added to an already existing {@code ConfigurationInterpolator}.
+     * Tests the events generated by a copy() operation.
      */
     @Test
-    public void testSetDefaultLookupsExistingInterpolator() {
-        final Lookup look = EasyMock.createMock(Lookup.class);
-        EasyMock.replay(look);
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.getInterpolator().addDefaultLookup(new ConfigurationLookup(new PropertiesConfiguration()));
-        config.setDefaultLookups(Collections.singleton(look));
-        final List<Lookup> lookups = config.getInterpolator().getDefaultLookups();
-        assertEquals("Wrong number of default lookups", 3, lookups.size());
-        assertSame("Wrong lookup at 1", look, lookups.get(1));
-        assertTrue("Wrong lookup at 2: " + lookups, lookups.get(2) instanceof ConfigurationLookup);
+    public void testCopyEvents() {
+        final AbstractConfiguration config = setUpDestConfig();
+        final Configuration srcConfig = setUpSourceConfig();
+        final CollectingConfigurationListener l = new CollectingConfigurationListener();
+        config.addEventListener(ConfigurationEvent.ANY, l);
+        config.copy(srcConfig);
+        checkCopyEvents(l, srcConfig, ConfigurationEvent.SET_PROPERTY);
     }
 
     /**
-     * Tests whether default lookups can be added if not {@code ConfigurationInterpolator} exists yet.
+     * Tests copying a null configuration. This should be a noop.
      */
     @Test
-    public void testSetDefaultLookupsNoInterpolator() {
-        final Lookup look = EasyMock.createMock(Lookup.class);
-        EasyMock.replay(look);
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.setInterpolator(null);
-        config.setDefaultLookups(Collections.singleton(look));
-        final List<Lookup> lookups = config.getInterpolator().getDefaultLookups();
-        assertEquals("Wrong number of default lookups", 2, lookups.size());
-        assertSame("Wrong lookup at 0", look, lookups.get(0));
-        assertTrue("Wrong lookup at 1", lookups.get(1) instanceof ConfigurationLookup);
+    public void testCopyNull() {
+        final AbstractConfiguration config = setUpDestConfig();
+        config.copy(null);
+        ConfigurationAssert.assertConfigurationEquals(setUpDestConfig(), config);
     }
 
     /**
-     * Tests whether a new {@code ConfigurationInterpolator} can be installed without providing custom lookups.
+     * Tests the copy() method if properties with multiple values and escaped list delimiters are involved.
      */
     @Test
-    public void testInstallInterpolatorNull() {
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.installInterpolator(null, null);
-        assertTrue("Got prefix lookups", config.getInterpolator().getLookups().isEmpty());
-        final List<Lookup> defLookups = config.getInterpolator().getDefaultLookups();
-        assertEquals("Wrong number of default lookups", 1, defLookups.size());
-        assertTrue("Wrong default lookup", defLookups.get(0) instanceof ConfigurationLookup);
+    public void testCopyWithLists() {
+        final Configuration srcConfig = setUpSourceConfig();
+        final AbstractConfiguration config = setUpDestConfig();
+        config.copy(srcConfig);
+        checkListProperties(config);
     }
 
     /**
-     * Tests whether a parent {@code ConfigurationInterpolator} can be set if already a {@code ConfigurationInterpolator} is
-     * available.
+     * Tests an interpolation that leads to a cycle. This should throw an exception.
      */
-    @Test
-    public void testSetParentInterpolatorExistingInterpolator() {
-        final ConfigurationInterpolator parent = EasyMock.createMock(ConfigurationInterpolator.class);
-        EasyMock.replay(parent);
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        final ConfigurationInterpolator ci = config.getInterpolator();
-        config.setParentInterpolator(parent);
-        assertSame("Parent was not set", parent, config.getInterpolator().getParentInterpolator());
-        assertSame("Interpolator was changed", ci, config.getInterpolator());
+    @Test(expected = IllegalStateException.class)
+    public void testCyclicInterpolation() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        config.addProperty("animal", "${animal_attr} ${species}");
+        config.addProperty("animal_attr", "quick brown");
+        config.addProperty("species", "${animal}");
+        config.addProperty(KEY_PREFIX, "This is a ${animal}");
+        config.getString(KEY_PREFIX);
     }
 
     /**
-     * Tests whether a parent {@code ConfigurationInterpolator} can be set if currently no {@code ConfigurationInterpolator}
-     * is available.
+     * Tests whether a configuration instance has a default conversion hander.
      */
     @Test
-    public void testSetParentInterpolatorNoInterpolator() {
-        final ConfigurationInterpolator parent = EasyMock.createMock(ConfigurationInterpolator.class);
-        EasyMock.replay(parent);
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.setInterpolator(null);
-        config.setParentInterpolator(parent);
-        assertSame("Parent was not set", parent, config.getInterpolator().getParentInterpolator());
+    public void testDefaultConversionHandler() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        assertEquals("Wrong default conversion handler", DefaultConversionHandler.class, config.getConversionHandler().getClass());
     }
 
     /**
-     * Tests whether a property can reference an array using interpolation. This is related to CONFIGURATION-633.
+     * Tests that the default conversion handler is shared between all configuration instances.
      */
     @Test
-    public void testInterpolateArray() {
+    public void testDefaultConversionHandlerSharedInstance() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final String[] values = {"some", "test", "values"};
-        final String keyArray = "testArray";
-        config.addProperty(keyArray, values);
-        config.addProperty(KEY_PREFIX, "${" + keyArray + "}");
-
-        assertArrayEquals("Wrong property", values, config.getStringArray(KEY_PREFIX));
+        final PropertiesConfiguration config2 = new PropertiesConfiguration();
+        assertSame("Multiple conversion handlers", config.getConversionHandler(), config2.getConversionHandler());
     }
 
     /**
-     * Tests whether a property can reference a list using interpolation. This is related to CONFIGURATION-633.
+     * Tests the default list delimiter hander.
      */
     @Test
-    public void testInterpolateList() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<String> values = Arrays.asList("some", "test", "values");
-        final String keyList = "testList";
-        config.addProperty(keyList, values);
-        config.addProperty(KEY_PREFIX, "${" + keyList + "}");
-
-        assertEquals("Wrong property", values, config.getList(String.class, KEY_PREFIX));
+    public void testDefaultListDelimiterHandler() {
+        final BaseConfiguration config = new BaseConfiguration();
+        assertTrue("Wrong list delimiter handler", config.getListDelimiterHandler() instanceof DisabledListDelimiterHandler);
     }
 
     /**
-     * Tests getList() for single non-string values.
+     * Tests the generic get() method.
      */
     @Test
-    public void testGetListNonString() {
-        checkGetListScalar(Integer.valueOf(42));
-        checkGetListScalar(Long.valueOf(42));
-        checkGetListScalar(Short.valueOf((short) 42));
-        checkGetListScalar(Byte.valueOf((byte) 42));
-        checkGetListScalar(Float.valueOf(42));
-        checkGetListScalar(Double.valueOf(42));
-        checkGetListScalar(Boolean.TRUE);
+    public void testGet() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        final Integer value = 20130816;
+        config.addProperty(KEY_PREFIX, value.toString());
+        assertEquals("Wrong result", value, config.get(Integer.class, KEY_PREFIX));
     }
 
     /**
-     * Tests getStringArray() for single son-string values.
+     * Tests whether conversion to an array is possible.
      */
     @Test
-    public void testGetStringArrayNonString() {
-        checkGetStringArrayScalar(Integer.valueOf(42));
-        checkGetStringArrayScalar(Long.valueOf(42));
-        checkGetStringArrayScalar(Short.valueOf((short) 42));
-        checkGetStringArrayScalar(Byte.valueOf((byte) 42));
-        checkGetStringArrayScalar(Float.valueOf(42));
-        checkGetStringArrayScalar(Double.valueOf(42));
-        checkGetStringArrayScalar(Boolean.TRUE);
+    public void testGetArray() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        final Integer[] expected = new Integer[PROP_COUNT];
+        for (int i = 0; i < PROP_COUNT; i++) {
+            config.addProperty(KEY_PREFIX, String.valueOf(i));
+            expected[i] = Integer.valueOf(i);
+        }
+        final Integer[] result = config.get(Integer[].class, KEY_PREFIX);
+        assertArrayEquals("Wrong result", expected, result);
     }
 
     /**
-     * Tests getStringArray() if the key cannot be found.
+     * Tests getArray() if the default value is not an array.
      */
-    @Test
-    public void testGetStringArrayUnknown() {
-        final BaseConfiguration config = new BaseConfiguration();
-        final String[] array = config.getStringArray(KEY_PREFIX);
-        assertEquals("Got elements", 0, array.length);
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetArrayDefaultValueNotAnArray() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        config.getArray(Integer.class, KEY_PREFIX, this);
     }
 
     /**
-     * Helper method for checking getList() if the property value is a scalar.
-     *
-     * @param value the value of the property
+     * Tests getArray() if the default value is an array with a different component type.
      */
-    private void checkGetListScalar(final Object value) {
-        final BaseConfiguration config = new BaseConfiguration();
-        config.addProperty(KEY_PREFIX, value);
-        final List<Object> lst = config.getList(KEY_PREFIX);
-        assertEquals("Wrong number of values", 1, lst.size());
-        assertEquals("Wrong value", value.toString(), lst.get(0));
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetArrayDefaultValueWrongComponentClass() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        config.getArray(Integer.class, KEY_PREFIX, new short[1]);
     }
 
     /**
-     * Helper method for checking getStringArray() if the property value is a scalar.
-     *
-     * @param value the value of the property
+     * Tests a conversion to an array of primitive types.
      */
-    private void checkGetStringArrayScalar(final Object value) {
-        final BaseConfiguration config = new BaseConfiguration();
-        config.addProperty(KEY_PREFIX, value);
-        final String[] array = config.getStringArray(KEY_PREFIX);
-        assertEquals("Weong number of elements", 1, array.length);
-        assertEquals("Wrong value", value.toString(), array[0]);
+    @Test
+    public void testGetArrayPrimitive() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        final short[] expected = new short[PROP_COUNT];
+        for (int i = 0; i < PROP_COUNT; i++) {
+            config.addProperty(KEY_PREFIX, String.valueOf(i));
+            expected[i] = (short) i;
+        }
+        final short[] result = config.get(short[].class, KEY_PREFIX, ArrayUtils.EMPTY_SHORT_ARRAY);
+        assertArrayEquals("Wrong result", expected, result);
     }
 
     /**
-     * Tests whether interpolation works in variable names.
+     * Tests get() for an unknown array property if no default value is provided.
      */
     @Test
-    public void testNestedVariableInterpolation() {
-        final BaseConfiguration config = new BaseConfiguration();
-        config.getInterpolator().setEnableSubstitutionInVariables(true);
-        config.addProperty("java.version", "1.4");
-        config.addProperty("jre-1.4", "C:\\java\\1.4");
-        config.addProperty("jre.path", "${jre-${java.version}}");
-        assertEquals("Wrong path", "C:\\java\\1.4", config.getString("jre.path"));
+    public void testGetArrayUnknownNoDefault() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        assertNull("Wrong result", config.get(Integer[].class, KEY_PREFIX));
     }
 
     /**
-     * Tries to set a null list delimiter handler.
+     * Tests get() for an unknown array property if a default value is provided.
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void testSetListDelimiterHandlerNull() {
-        final BaseConfiguration config = new BaseConfiguration();
-        config.setListDelimiterHandler(null);
+    @Test
+    public void testGetArrayUnknownWithDefault() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        final int[] defValue = {1, 2, 3};
+        assertArrayEquals("Wrong result", defValue, config.get(int[].class, KEY_PREFIX, defValue));
     }
 
     /**
-     * Tests the default list delimiter hander.
+     * Tests a conversion to a collection.
      */
     @Test
-    public void testDefaultListDelimiterHandler() {
-        final BaseConfiguration config = new BaseConfiguration();
-        assertTrue("Wrong list delimiter handler", config.getListDelimiterHandler() instanceof DisabledListDelimiterHandler);
+    public void testGetCollection() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        final List<Integer> expected = prepareListTest(config);
+        final List<Integer> result = new ArrayList<>(PROP_COUNT);
+        assertSame("Wrong result", result, config.getCollection(Integer.class, KEY_PREFIX, result));
+        assertEquals("Wrong converted content", expected, result);
     }
 
     /**
-     * Tests the interpolation features.
+     * Tests getCollection() if no target collection is provided.
      */
     @Test
-    public void testInterpolateString() {
+    public void testGetCollectionNullTarget() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty("animal", "quick brown fox");
-        config.addProperty("target", "lazy dog");
-        config.addProperty(KEY_PREFIX, SUBST_TXT);
-        assertEquals("Wrong interpolation", "The quick brown fox jumps over the lazy dog.", config.getString(KEY_PREFIX));
+        final List<Integer> expected = prepareListTest(config);
+        final Collection<Integer> result = config.getCollection(Integer.class, KEY_PREFIX, null, new ArrayList<>());
+        assertEquals("Wrong result", expected, result);
     }
 
     /**
-     * Tests complex interpolation where the variables' values contain in turn other variables.
+     * Tests whether a single value property can be converted to a collection.
      */
     @Test
-    public void testInterpolateRecursive() {
+    public void testGetCollectionSingleValue() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty("animal", "${animal_attr} fox");
-        config.addProperty("target", "${target_attr} dog");
-        config.addProperty("animal_attr", "quick brown");
-        config.addProperty("target_attr", "lazy");
-        config.addProperty(KEY_PREFIX, SUBST_TXT);
-        assertEquals("Wrong complex interpolation", "The quick brown fox jumps over the lazy dog.", config.getString(KEY_PREFIX));
+        config.addProperty(KEY_PREFIX, "1");
+        final List<Integer> result = new ArrayList<>(1);
+        config.getCollection(Integer.class, KEY_PREFIX, result);
+        assertEquals("Wrong number of elements", 1, result.size());
+        assertEquals("Wrong element", Integer.valueOf(1), result.get(0));
     }
 
     /**
-     * Tests an interpolation that leads to a cycle. This should throw an exception.
+     * Tests getCollection() for an unknown property if no default value is provided.
      */
-    @Test(expected = IllegalStateException.class)
-    public void testCyclicInterpolation() {
+    @Test
+    public void testGetCollectionUnknownNoDefault() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty("animal", "${animal_attr} ${species}");
-        config.addProperty("animal_attr", "quick brown");
-        config.addProperty("species", "${animal}");
-        config.addProperty(KEY_PREFIX, "This is a ${animal}");
-        config.getString(KEY_PREFIX);
+        final List<Integer> result = new ArrayList<>();
+        assertNull("Wrong result", config.getCollection(Integer.class, KEY_PREFIX, result));
+        assertTrue("Got elements", result.isEmpty());
     }
 
     /**
-     * Tests interpolation if a variable is unknown. Then the variable won't be substituted.
+     * Tests getCollection() for an unknown property if a default collection is provided.
      */
     @Test
-    public void testInterpolationUnknownVariable() {
+    public void testGetCollectionUnknownWithDefault() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty("animal", "quick brown fox");
-        config.addProperty(KEY_PREFIX, SUBST_TXT);
-        assertEquals("Wrong interpolation", "The quick brown fox jumps over the ${target}.", config.getString(KEY_PREFIX));
+        final List<Integer> defValue = Arrays.asList(1, 2, 4, 8, 16, 32);
+        final Collection<Integer> result = config.getCollection(Integer.class, KEY_PREFIX, null, defValue);
+        assertEquals("Wrong result", defValue, result);
     }
 
     /**
-     * Tests interpolate() if the configuration does not have a {@code ConfigurationInterpolator}.
+     * Tries to query an encoded string without a decoder.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetEncodedStringNoDecoder() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        config.getEncodedString(KEY_PREFIX, null);
+    }
+
+    /**
+     * Tries to query an encoded string with the default decoder if this property is not defined.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testGetEncodedStringNoDefaultDecoderDefined() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        config.getEncodedString(KEY_PREFIX);
+    }
+
+    /**
+     * Tests whether undefined keys are handled when querying encoded strings.
      */
     @Test
-    public void testInterpolationNoInterpolator() {
+    public void testGetEncodedStringNoValue() {
+        final ConfigurationDecoder decoder = EasyMock.createMock(ConfigurationDecoder.class);
+        EasyMock.replay(decoder);
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty("animal", "quick brown fox");
-        config.addProperty("target", "lazy dog");
-        config.addProperty(KEY_PREFIX, SUBST_TXT);
-        config.setInterpolator(null);
-        assertEquals("Interpolation was performed", SUBST_TXT, config.getString(KEY_PREFIX));
+        assertNull("Got a value", config.getEncodedString(KEY_PREFIX, decoder));
     }
 
     /**
-     * Tests whether a configuration instance has a default conversion hander.
+     * Tests whether an encoded value can be retrieved.
      */
     @Test
-    public void testDefaultConversionHandler() {
+    public void testGetEncodedStringValue() {
+        final ConfigurationDecoder decoder = EasyMock.createMock(ConfigurationDecoder.class);
+        final String value = "original value";
+        final String decodedValue = "decoded value";
+        EasyMock.expect(decoder.decode(value)).andReturn(decodedValue);
+        EasyMock.replay(decoder);
+
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        assertEquals("Wrong default conversion handler", DefaultConversionHandler.class, config.getConversionHandler().getClass());
+        config.addProperty(KEY_PREFIX, value);
+        assertEquals("Wrong decoded value", decodedValue, config.getEncodedString(KEY_PREFIX, decoder));
     }
 
     /**
-     * Tests that the default conversion handler is shared between all configuration instances.
+     * Tests whether a default decoder can be set which is queried for encoded strings.
      */
     @Test
-    public void testDefaultConversionHandlerSharedInstance() {
+    public void testGetEncodedStringWithDefaultDecoder() {
+        final ConfigurationDecoder decoder = EasyMock.createMock(ConfigurationDecoder.class);
+        final String value = "original value";
+        final String decodedValue = "decoded value";
+        EasyMock.expect(decoder.decode(value)).andReturn(decodedValue);
+        EasyMock.replay(decoder);
+
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final PropertiesConfiguration config2 = new PropertiesConfiguration();
-        assertSame("Multiple conversion handlers", config.getConversionHandler(), config2.getConversionHandler());
+        config.setConfigurationDecoder(decoder);
+        config.addProperty(KEY_PREFIX, value);
+        assertEquals("Wrong decoded value", decodedValue, config.getEncodedString(KEY_PREFIX));
     }
 
     /**
-     * Tests whether the conversion handler can be changed.
+     * Tests a conversion to a list.
      */
     @Test
-    public void testSetDefaultConversionHandler() {
+    public void testGetList() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final ConversionHandler handler = new DefaultConversionHandler();
-        config.setConversionHandler(handler);
-        assertSame("Handler not set", handler, config.getConversionHandler());
+        final List<Integer> expected = prepareListTest(config);
+        final List<Integer> result = config.getList(Integer.class, KEY_PREFIX);
+        assertEquals("Wrong result", expected, result);
     }
 
     /**
-     * Tries to set a null value for the conversion handler.
+     * Tests getList() for single non-string values.
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void testSetDefaultConversionHandlerNull() {
-        new PropertiesConfiguration().setConversionHandler(null);
+    @Test
+    public void testGetListNonString() {
+        checkGetListScalar(Integer.valueOf(42));
+        checkGetListScalar(Long.valueOf(42));
+        checkGetListScalar(Short.valueOf((short) 42));
+        checkGetListScalar(Byte.valueOf((byte) 42));
+        checkGetListScalar(Float.valueOf(42));
+        checkGetListScalar(Double.valueOf(42));
+        checkGetListScalar(Boolean.TRUE);
     }
 
     /**
-     * Tests the generic get() method.
+     * Tests a conversion to a list if the property is unknown and no default value is provided.
      */
     @Test
-    public void testGet() {
+    public void testGetListUnknownNoDefault() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final Integer value = 20130816;
-        config.addProperty(KEY_PREFIX, value.toString());
-        assertEquals("Wrong result", value, config.get(Integer.class, KEY_PREFIX));
+        assertNull("Wrong result", config.getList(Integer.class, KEY_PREFIX));
+    }
+
+    /**
+     * Tests a conversion to a list if the property is unknown and a default list is provided.
+     */
+    @Test
+    public void testGetListUnknownWithDefault() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        final List<Integer> defValue = Arrays.asList(1, 2, 3);
+        assertEquals("Wrong result", defValue, config.getList(Integer.class, KEY_PREFIX, defValue));
+    }
+
+    /**
+     * Tests getStringArray() for single son-string values.
+     */
+    @Test
+    public void testGetStringArrayNonString() {
+        checkGetStringArrayScalar(Integer.valueOf(42));
+        checkGetStringArrayScalar(Long.valueOf(42));
+        checkGetStringArrayScalar(Short.valueOf((short) 42));
+        checkGetStringArrayScalar(Byte.valueOf((byte) 42));
+        checkGetStringArrayScalar(Float.valueOf(42));
+        checkGetStringArrayScalar(Double.valueOf(42));
+        checkGetStringArrayScalar(Boolean.TRUE);
+    }
+
+    /**
+     * Tests getStringArray() if the key cannot be found.
+     */
+    @Test
+    public void testGetStringArrayUnknown() {
+        final BaseConfiguration config = new BaseConfiguration();
+        final String[] array = config.getStringArray(KEY_PREFIX);
+        assertEquals("Got elements", 0, array.length);
     }
 
     /**
@@ -653,236 +771,249 @@ public class TestAbstractConfigurationBasicFeatures {
     }
 
     /**
-     * Tests whether conversion to an array is possible.
+     * Tests whether a new {@code ConfigurationInterpolator} can be installed without providing custom lookups.
      */
     @Test
-    public void testGetArray() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        final Integer[] expected = new Integer[PROP_COUNT];
-        for (int i = 0; i < PROP_COUNT; i++) {
-            config.addProperty(KEY_PREFIX, String.valueOf(i));
-            expected[i] = Integer.valueOf(i);
-        }
-        final Integer[] result = config.get(Integer[].class, KEY_PREFIX);
-        assertArrayEquals("Wrong result", expected, result);
+    public void testInstallInterpolatorNull() {
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.installInterpolator(null, null);
+        assertTrue("Got prefix lookups", config.getInterpolator().getLookups().isEmpty());
+        final List<Lookup> defLookups = config.getInterpolator().getDefaultLookups();
+        assertEquals("Wrong number of default lookups", 1, defLookups.size());
+        assertTrue("Wrong default lookup", defLookups.get(0) instanceof ConfigurationLookup);
     }
 
     /**
-     * Tests a conversion to an array of primitive types.
+     * Tests whether a property can reference an array using interpolation. This is related to CONFIGURATION-633.
      */
     @Test
-    public void testGetArrayPrimitive() {
+    public void testInterpolateArray() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final short[] expected = new short[PROP_COUNT];
-        for (int i = 0; i < PROP_COUNT; i++) {
-            config.addProperty(KEY_PREFIX, String.valueOf(i));
-            expected[i] = (short) i;
-        }
-        final short[] result = config.get(short[].class, KEY_PREFIX, ArrayUtils.EMPTY_SHORT_ARRAY);
-        assertArrayEquals("Wrong result", expected, result);
-    }
+        final String[] values = {"some", "test", "values"};
+        final String keyArray = "testArray";
+        config.addProperty(keyArray, values);
+        config.addProperty(KEY_PREFIX, "${" + keyArray + "}");
 
-    /**
-     * Tests get() for an unknown array property if no default value is provided.
-     */
-    @Test
-    public void testGetArrayUnknownNoDefault() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        assertNull("Wrong result", config.get(Integer[].class, KEY_PREFIX));
+        assertArrayEquals("Wrong property", values, config.getStringArray(KEY_PREFIX));
     }
 
     /**
-     * Tests get() for an unknown array property if a default value is provided.
+     * Tests whether environment variables can be interpolated.
      */
     @Test
-    public void testGetArrayUnknownWithDefault() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        final int[] defValue = {1, 2, 3};
-        assertArrayEquals("Wrong result", defValue, config.get(int[].class, KEY_PREFIX, defValue));
+    public void testInterpolateEnvironmentVariables() {
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        InterpolationTestHelper.testInterpolationEnvironment(config);
     }
 
     /**
-     * Tests getArray() if the default value is not an array.
+     * Tests escaping the variable marker, so that no interpolation will be performed.
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetArrayDefaultValueNotAnArray() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.getArray(Integer.class, KEY_PREFIX, this);
+    @Test
+    public void testInterpolateEscape() {
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        config.addProperty("mypath", "$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar\\,$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar");
+        assertEquals("Wrong interpolated value", "${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar,${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar",
+            config.getString("mypath"));
     }
 
     /**
-     * Tests getArray() if the default value is an array with a different component type.
+     * Tests whether a property can reference a list using interpolation. This is related to CONFIGURATION-633.
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetArrayDefaultValueWrongComponentClass() {
+    @Test
+    public void testInterpolateList() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.getArray(Integer.class, KEY_PREFIX, new short[1]);
+        final List<String> values = Arrays.asList("some", "test", "values");
+        final String keyList = "testList";
+        config.addProperty(keyList, values);
+        config.addProperty(KEY_PREFIX, "${" + keyList + "}");
+
+        assertEquals("Wrong property", values, config.getList(String.class, KEY_PREFIX));
     }
 
     /**
-     * Prepares a test configuration for a test for a list conversion. The configuration is populated with a list property.
-     * The returned list contains the expected list values converted to integers.
-     *
-     * @param config the test configuration
-     * @return the list with expected values
+     * Tests complex interpolation where the variables' values contain in turn other variables.
      */
-    private static List<Integer> prepareListTest(final PropertiesConfiguration config) {
-        final List<Integer> expected = new ArrayList<>(PROP_COUNT);
-        for (int i = 0; i < PROP_COUNT; i++) {
-            config.addProperty(KEY_PREFIX, String.valueOf(i));
-            expected.add(i);
-        }
-        return expected;
+    @Test
+    public void testInterpolateRecursive() {
+        final PropertiesConfiguration config = new PropertiesConfiguration();
+        config.addProperty("animal", "${animal_attr} fox");
+        config.addProperty("target", "${target_attr} dog");
+        config.addProperty("animal_attr", "quick brown");
+        config.addProperty("target_attr", "lazy");
+        config.addProperty(KEY_PREFIX, SUBST_TXT);
+        assertEquals("Wrong complex interpolation", "The quick brown fox jumps over the lazy dog.", config.getString(KEY_PREFIX));
     }
 
     /**
-     * Tests a conversion to a list.
+     * Tests the interpolation features.
      */
     @Test
-    public void testGetList() {
+    public void testInterpolateString() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<Integer> expected = prepareListTest(config);
-        final List<Integer> result = config.getList(Integer.class, KEY_PREFIX);
-        assertEquals("Wrong result", expected, result);
+        config.addProperty("animal", "quick brown fox");
+        config.addProperty("target", "lazy dog");
+        config.addProperty(KEY_PREFIX, SUBST_TXT);
+        assertEquals("Wrong interpolation", "The quick brown fox jumps over the lazy dog.", config.getString(KEY_PREFIX));
     }
 
     /**
-     * Tests a conversion to a list if the property is unknown and no default value is provided.
+     * Tests interpolate() if the configuration does not have a {@code ConfigurationInterpolator}.
      */
     @Test
-    public void testGetListUnknownNoDefault() {
+    public void testInterpolationNoInterpolator() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        assertNull("Wrong result", config.getList(Integer.class, KEY_PREFIX));
+        config.addProperty("animal", "quick brown fox");
+        config.addProperty("target", "lazy dog");
+        config.addProperty(KEY_PREFIX, SUBST_TXT);
+        config.setInterpolator(null);
+        assertEquals("Interpolation was performed", SUBST_TXT, config.getString(KEY_PREFIX));
     }
 
     /**
-     * Tests a conversion to a list if the property is unknown and a default list is provided.
+     * Tests interpolation if a variable is unknown. Then the variable won't be substituted.
      */
     @Test
-    public void testGetListUnknownWithDefault() {
+    public void testInterpolationUnknownVariable() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<Integer> defValue = Arrays.asList(1, 2, 3);
-        assertEquals("Wrong result", defValue, config.getList(Integer.class, KEY_PREFIX, defValue));
+        config.addProperty("animal", "quick brown fox");
+        config.addProperty(KEY_PREFIX, SUBST_TXT);
+        assertEquals("Wrong interpolation", "The quick brown fox jumps over the ${target}.", config.getString(KEY_PREFIX));
     }
 
     /**
-     * Tests a conversion to a collection.
+     * Tests whether interpolation works in variable names.
      */
     @Test
-    public void testGetCollection() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<Integer> expected = prepareListTest(config);
-        final List<Integer> result = new ArrayList<>(PROP_COUNT);
-        assertSame("Wrong result", result, config.getCollection(Integer.class, KEY_PREFIX, result));
-        assertEquals("Wrong converted content", expected, result);
+    public void testNestedVariableInterpolation() {
+        final BaseConfiguration config = new BaseConfiguration();
+        config.getInterpolator().setEnableSubstitutionInVariables(true);
+        config.addProperty("java.version", "1.4");
+        config.addProperty("jre-1.4", "C:\\java\\1.4");
+        config.addProperty("jre.path", "${jre-${java.version}}");
+        assertEquals("Wrong path", "C:\\java\\1.4", config.getString("jre.path"));
     }
 
     /**
-     * Tests getCollection() if no target collection is provided.
+     * Tests whether the conversion handler can be changed.
      */
     @Test
-    public void testGetCollectionNullTarget() {
+    public void testSetDefaultConversionHandler() {
         final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<Integer> expected = prepareListTest(config);
-        final Collection<Integer> result = config.getCollection(Integer.class, KEY_PREFIX, null, new ArrayList<>());
-        assertEquals("Wrong result", expected, result);
+        final ConversionHandler handler = new DefaultConversionHandler();
+        config.setConversionHandler(handler);
+        assertSame("Handler not set", handler, config.getConversionHandler());
     }
 
     /**
-     * Tests whether a single value property can be converted to a collection.
+     * Tries to set a null value for the conversion handler.
      */
-    @Test
-    public void testGetCollectionSingleValue() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty(KEY_PREFIX, "1");
-        final List<Integer> result = new ArrayList<>(1);
-        config.getCollection(Integer.class, KEY_PREFIX, result);
-        assertEquals("Wrong number of elements", 1, result.size());
-        assertEquals("Wrong element", Integer.valueOf(1), result.get(0));
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetDefaultConversionHandlerNull() {
+        new PropertiesConfiguration().setConversionHandler(null);
     }
 
     /**
-     * Tests getCollection() for an unknown property if no default value is provided.
+     * Tests whether default lookups can be added to an already existing {@code ConfigurationInterpolator}.
      */
     @Test
-    public void testGetCollectionUnknownNoDefault() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<Integer> result = new ArrayList<>();
-        assertNull("Wrong result", config.getCollection(Integer.class, KEY_PREFIX, result));
-        assertTrue("Got elements", result.isEmpty());
+    public void testSetDefaultLookupsExistingInterpolator() {
+        final Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.getInterpolator().addDefaultLookup(new ConfigurationLookup(new PropertiesConfiguration()));
+        config.setDefaultLookups(Collections.singleton(look));
+        final List<Lookup> lookups = config.getInterpolator().getDefaultLookups();
+        assertEquals("Wrong number of default lookups", 3, lookups.size());
+        assertSame("Wrong lookup at 1", look, lookups.get(1));
+        assertTrue("Wrong lookup at 2: " + lookups, lookups.get(2) instanceof ConfigurationLookup);
     }
 
     /**
-     * Tests getCollection() for an unknown property if a default collection is provided.
+     * Tests whether default lookups can be added if not {@code ConfigurationInterpolator} exists yet.
      */
     @Test
-    public void testGetCollectionUnknownWithDefault() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        final List<Integer> defValue = Arrays.asList(1, 2, 4, 8, 16, 32);
-        final Collection<Integer> result = config.getCollection(Integer.class, KEY_PREFIX, null, defValue);
-        assertEquals("Wrong result", defValue, result);
+    public void testSetDefaultLookupsNoInterpolator() {
+        final Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setInterpolator(null);
+        config.setDefaultLookups(Collections.singleton(look));
+        final List<Lookup> lookups = config.getInterpolator().getDefaultLookups();
+        assertEquals("Wrong number of default lookups", 2, lookups.size());
+        assertSame("Wrong lookup at 0", look, lookups.get(0));
+        assertTrue("Wrong lookup at 1", lookups.get(1) instanceof ConfigurationLookup);
     }
 
     /**
-     * Tries to query an encoded string without a decoder.
+     * Tries to set a null list delimiter handler.
      */
     @Test(expected = IllegalArgumentException.class)
-    public void testGetEncodedStringNoDecoder() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.getEncodedString(KEY_PREFIX, null);
+    public void testSetListDelimiterHandlerNull() {
+        final BaseConfiguration config = new BaseConfiguration();
+        config.setListDelimiterHandler(null);
     }
 
     /**
-     * Tests whether undefined keys are handled when querying encoded strings.
+     * Tests whether a parent {@code ConfigurationInterpolator} can be set if already a {@code ConfigurationInterpolator} is
+     * available.
      */
     @Test
-    public void testGetEncodedStringNoValue() {
-        final ConfigurationDecoder decoder = EasyMock.createMock(ConfigurationDecoder.class);
-        EasyMock.replay(decoder);
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        assertNull("Got a value", config.getEncodedString(KEY_PREFIX, decoder));
+    public void testSetParentInterpolatorExistingInterpolator() {
+        final ConfigurationInterpolator parent = EasyMock.createMock(ConfigurationInterpolator.class);
+        EasyMock.replay(parent);
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        final ConfigurationInterpolator ci = config.getInterpolator();
+        config.setParentInterpolator(parent);
+        assertSame("Parent was not set", parent, config.getInterpolator().getParentInterpolator());
+        assertSame("Interpolator was changed", ci, config.getInterpolator());
     }
 
     /**
-     * Tests whether an encoded value can be retrieved.
+     * Tests whether a parent {@code ConfigurationInterpolator} can be set if currently no {@code ConfigurationInterpolator}
+     * is available.
      */
     @Test
-    public void testGetEncodedStringValue() {
-        final ConfigurationDecoder decoder = EasyMock.createMock(ConfigurationDecoder.class);
-        final String value = "original value";
-        final String decodedValue = "decoded value";
-        EasyMock.expect(decoder.decode(value)).andReturn(decodedValue);
-        EasyMock.replay(decoder);
-
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.addProperty(KEY_PREFIX, value);
-        assertEquals("Wrong decoded value", decodedValue, config.getEncodedString(KEY_PREFIX, decoder));
+    public void testSetParentInterpolatorNoInterpolator() {
+        final ConfigurationInterpolator parent = EasyMock.createMock(ConfigurationInterpolator.class);
+        EasyMock.replay(parent);
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setInterpolator(null);
+        config.setParentInterpolator(parent);
+        assertSame("Parent was not set", parent, config.getInterpolator().getParentInterpolator());
     }
 
     /**
-     * Tries to query an encoded string with the default decoder if this property is not defined.
+     * Tests whether prefix lookups can be added to an existing {@code ConfigurationInterpolator}.
      */
-    @Test(expected = IllegalStateException.class)
-    public void testGetEncodedStringNoDefaultDecoderDefined() {
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.getEncodedString(KEY_PREFIX);
+    @Test
+    public void testSetPrefixLookupsExistingInterpolator() {
+        final Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        final int count = config.getInterpolator().getLookups().size();
+        final Map<String, Lookup> lookups = new HashMap<>();
+        lookups.put("test", look);
+        config.setPrefixLookups(lookups);
+        final Map<String, Lookup> lookups2 = config.getInterpolator().getLookups();
+        assertEquals("Not added", count + 1, lookups2.size());
+        assertSame("Not found", look, lookups2.get("test"));
     }
 
     /**
-     * Tests whether a default decoder can be set which is queried for encoded strings.
+     * Tests whether prefix lookups can be added if no {@code ConfigurationInterpolator} exists yet.
      */
     @Test
-    public void testGetEncodedStringWithDefaultDecoder() {
-        final ConfigurationDecoder decoder = EasyMock.createMock(ConfigurationDecoder.class);
-        final String value = "original value";
-        final String decodedValue = "decoded value";
-        EasyMock.expect(decoder.decode(value)).andReturn(decodedValue);
-        EasyMock.replay(decoder);
-
-        final PropertiesConfiguration config = new PropertiesConfiguration();
-        config.setConfigurationDecoder(decoder);
-        config.addProperty(KEY_PREFIX, value);
-        assertEquals("Wrong decoded value", decodedValue, config.getEncodedString(KEY_PREFIX));
+    public void testSetPrefixLookupsNoInterpolator() {
+        final Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setInterpolator(null);
+        config.setPrefixLookups(Collections.singletonMap("test", look));
+        final Map<String, Lookup> lookups = config.getInterpolator().getLookups();
+        assertEquals("Wrong number of lookups", 1, lookups.size());
+        assertSame("Not found", look, lookups.get("test"));
     }
 
     /**
@@ -896,135 +1027,4 @@ public class TestAbstractConfigurationBasicFeatures {
         }
         assertEquals("Wrong size", PROP_COUNT, config.size());
     }
-
-    /**
-     * Creates the source configuration for testing the copy() and append() methods. This configuration contains keys with
-     * an odd index and values starting with the prefix "src". There are also some list properties.
-     *
-     * @return the source configuration for copy operations
-     */
-    private Configuration setUpSourceConfig() {
-        final BaseConfiguration config = new BaseConfiguration();
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        for (int i = 1; i < PROP_COUNT; i += 2) {
-            config.addProperty(KEY_PREFIX + i, "src" + i);
-        }
-        config.addProperty("list1", "1,2,3");
-        config.addProperty("list2", "3\\,1415,9\\,81");
-        return config;
-    }
-
-    /**
-     * Creates the destination configuration for testing the copy() and append() methods. This configuration contains keys
-     * with a running index and corresponding values starting with the prefix "value".
-     *
-     * @return the destination configuration for copy operations
-     */
-    private AbstractConfiguration setUpDestConfig() {
-        final AbstractConfiguration config = new TestConfigurationImpl(new PropertiesConfiguration());
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        for (int i = 0; i < PROP_COUNT; i++) {
-            config.addProperty(KEY_PREFIX + i, "value" + i);
-        }
-        return config;
-    }
-
-    /**
-     * Tests the values of list properties after a copy operation.
-     *
-     * @param config the configuration to test
-     */
-    private void checkListProperties(final Configuration config) {
-        List<Object> values = config.getList("list1");
-        assertEquals("Wrong number of elements in list 1", 3, values.size());
-        values = config.getList("list2");
-        assertEquals("Wrong number of elements in list 2", 2, values.size());
-        assertEquals("Wrong value 1", "3,1415", values.get(0));
-        assertEquals("Wrong value 2", "9,81", values.get(1));
-    }
-
-    /**
-     * Tests whether the correct events are received for a copy operation.
-     *
-     * @param l the event listener
-     * @param src the configuration that was copied
-     * @param eventType the expected event type
-     */
-    private void checkCopyEvents(final CollectingConfigurationListener l, final Configuration src, final EventType<?> eventType) {
-        final Map<String, ConfigurationEvent> events = new HashMap<>();
-        for (final ConfigurationEvent e : l.events) {
-            assertEquals("Wrong event type", eventType, e.getEventType());
-            assertTrue("Unknown property: " + e.getPropertyName(), src.containsKey(e.getPropertyName()));
-            if (!e.isBeforeUpdate()) {
-                assertTrue("After event without before event", events.containsKey(e.getPropertyName()));
-            } else {
-                events.put(e.getPropertyName(), e);
-            }
-        }
-
-        for (final Iterator<String> it = src.getKeys(); it.hasNext();) {
-            final String key = it.next();
-            assertTrue("No event received for key " + key, events.containsKey(key));
-        }
-    }
-
-    /**
-     * A test configuration implementation. This implementation inherits directly from AbstractConfiguration. For
-     * implementing the required functionality another implementation of AbstractConfiguration is used; all methods that
-     * need to be implemented delegate to this wrapped configuration.
-     */
-    static class TestConfigurationImpl extends AbstractConfiguration {
-        /** Stores the underlying configuration. */
-        private final AbstractConfiguration config;
-
-        public AbstractConfiguration getUnderlyingConfiguration() {
-            return config;
-        }
-
-        public TestConfigurationImpl(final AbstractConfiguration wrappedConfig) {
-            config = wrappedConfig;
-        }
-
-        @Override
-        protected void addPropertyDirect(final String key, final Object value) {
-            config.addPropertyDirect(key, value);
-        }
-
-        @Override
-        protected boolean containsKeyInternal(final String key) {
-            return config.containsKey(key);
-        }
-
-        @Override
-        protected Iterator<String> getKeysInternal() {
-            return config.getKeys();
-        }
-
-        @Override
-        protected Object getPropertyInternal(final String key) {
-            return config.getProperty(key);
-        }
-
-        @Override
-        protected boolean isEmptyInternal() {
-            return config.isEmpty();
-        }
-
-        @Override
-        protected void clearPropertyDirect(final String key) {
-            config.clearPropertyDirect(key);
-        }
-    }
-
-    /**
-     * An event listener implementation that simply collects all received configuration events.
-     */
-    private static class CollectingConfigurationListener implements EventListener<ConfigurationEvent> {
-        final List<ConfigurationEvent> events = new ArrayList<>();
-
-        @Override
-        public void onEvent(final ConfigurationEvent event) {
-            events.add(event);
-        }
-    }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationSynchronization.java b/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationSynchronization.java
index 4660c39..8c0d077 100644
--- a/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationSynchronization.java
+++ b/src/test/java/org/apache/commons/configuration2/TestAbstractConfigurationSynchronization.java
@@ -40,6 +40,20 @@ public class TestAbstractConfigurationSynchronization {
     /** Constant for the test property accessed by all tests. */
     private static final String PROP = "configuration.loaded";
 
+    /**
+     * Prepares a mock configuration for a copy operation.
+     *
+     * @return the mock configuration
+     */
+    private static Configuration prepareConfigurationMockForCopy() {
+        final Configuration config2 = EasyMock.createStrictMock(Configuration.class);
+        config2.lock(LockMode.READ);
+        EasyMock.expect(config2.getKeys()).andReturn(Collections.<String>emptySet().iterator());
+        config2.unlock(LockMode.READ);
+        EasyMock.replay(config2);
+        return config2;
+    }
+
     /** The synchronizer used for testing. */
     private SynchronizerTestImpl sync;
 
@@ -57,93 +71,86 @@ public class TestAbstractConfigurationSynchronization {
     }
 
     /**
-     * Tests the Synchronizer used by default.
+     * Tests the correct synchronization of addProperty().
      */
     @Test
-    public void testDefaultSynchronizer() {
-        assertSame("Wrong default synchronizer", NoOpSynchronizer.INSTANCE, new PropertiesConfiguration().getSynchronizer());
+    public void testAddPropertySynchronized() {
+        config.addProperty(PROP, "of course");
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     /**
-     * Tests whether a read lock can be obtained.
+     * Tests whether the append() method uses synchronization.
      */
     @Test
-    public void testLockRead() {
-        config.lock(LockMode.READ);
-        sync.verify(Methods.BEGIN_READ);
+    public void testAppendSynchronized() {
+        final Configuration config2 = prepareConfigurationMockForCopy();
+        config.append(config2);
+        EasyMock.verify(config2);
     }
 
     /**
-     * Tests whether a write lock can be obtained.
+     * Tests the correct synchronization of clearProperty().
      */
     @Test
-    public void testLockWrite() {
-        config.lock(LockMode.WRITE);
-        sync.verify(Methods.BEGIN_WRITE);
-    }
-
-    /**
-     * Tests lock() with a null argument.
-     */
-    @Test(expected = NullPointerException.class)
-    public void testLockNull() {
-        config.lock(null);
+    public void testClearPropertySynchronized() {
+        config.clearProperty(PROP);
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     /**
-     * Tests whether a read lock can be released.
+     * Tests the correct synchronization of clear().
      */
     @Test
-    public void testUnlockRead() {
-        config.unlock(LockMode.READ);
-        sync.verify(Methods.END_READ);
+    public void testClearSynchronized() {
+        config.clear();
+        sync.verifyStart(Methods.BEGIN_WRITE);
+        sync.verifyEnd(Methods.END_WRITE);
     }
 
     /**
-     * Tests whether a write lock can be released.
+     * Tests whether containsKey() is correctly synchronized.
      */
     @Test
-    public void testUnlockWrite() {
-        config.unlock(LockMode.WRITE);
-        sync.verify(Methods.END_WRITE);
+    public void testContainsKeySychronized() {
+        assertTrue("Wrong result", config.containsKey(PROP));
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
-     * Tests the correct synchronization of addProperty().
+     * Tests whether the copy() method uses synchronization.
      */
     @Test
-    public void testAddPropertySynchronized() {
-        config.addProperty(PROP, "of course");
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+    public void testCopySynchronized() {
+        final Configuration config2 = prepareConfigurationMockForCopy();
+        config.copy(config2);
+        EasyMock.verify(config2);
     }
 
     /**
-     * Tests the correct synchronization of setProperty().
+     * Tests the Synchronizer used by default.
      */
     @Test
-    public void testSetPropertySynchronized() {
-        config.setProperty(PROP, "yes");
-        sync.verifyStart(Methods.BEGIN_WRITE);
-        sync.verifyEnd(Methods.END_WRITE);
+    public void testDefaultSynchronizer() {
+        assertSame("Wrong default synchronizer", NoOpSynchronizer.INSTANCE, new PropertiesConfiguration().getSynchronizer());
     }
 
     /**
-     * Tests the correct synchronization of clearProperty().
+     * Tests whether getKeys(String prefix) is correctly synchronized.
      */
     @Test
-    public void testClearPropertySynchronized() {
-        config.clearProperty(PROP);
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+    public void testGetKeysPrefixSynchronized() {
+        config.getKeys("test");
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
-     * Tests the correct synchronization of clear().
+     * Tests whether getKeys() is correctly synchronized.
      */
     @Test
-    public void testClearSynchronized() {
-        config.clear();
-        sync.verifyStart(Methods.BEGIN_WRITE);
-        sync.verifyEnd(Methods.END_WRITE);
+    public void testGetKeysSynchronized() {
+        assertTrue("No keys", config.getKeys().hasNext());
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
@@ -157,47 +164,56 @@ public class TestAbstractConfigurationSynchronization {
     }
 
     /**
-     * Tests whether containsKey() is correctly synchronized.
+     * Tests whether isEmpty() is correctly synchronized.
      */
     @Test
-    public void testContainsKeySychronized() {
-        assertTrue("Wrong result", config.containsKey(PROP));
+    public void testIsEmptySynchronized() {
+        assertFalse("Configuration is empty", config.isEmpty());
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
-     * Tests whether isEmpty() is correctly synchronized.
+     * Tests lock() with a null argument.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testLockNull() {
+        config.lock(null);
+    }
+
+    /**
+     * Tests whether a read lock can be obtained.
      */
     @Test
-    public void testIsEmptySynchronized() {
-        assertFalse("Configuration is empty", config.isEmpty());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+    public void testLockRead() {
+        config.lock(LockMode.READ);
+        sync.verify(Methods.BEGIN_READ);
     }
 
     /**
-     * Tests whether size() is correctly synchronized.
+     * Tests whether a write lock can be obtained.
      */
     @Test
-    public void testSizeSynchronized() {
-        assertFalse("Wrong size", config.isEmpty());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+    public void testLockWrite() {
+        config.lock(LockMode.WRITE);
+        sync.verify(Methods.BEGIN_WRITE);
     }
 
     /**
-     * Tests whether getKeys() is correctly synchronized.
+     * Tests the correct synchronization of setProperty().
      */
     @Test
-    public void testGetKeysSynchronized() {
-        assertTrue("No keys", config.getKeys().hasNext());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+    public void testSetPropertySynchronized() {
+        config.setProperty(PROP, "yes");
+        sync.verifyStart(Methods.BEGIN_WRITE);
+        sync.verifyEnd(Methods.END_WRITE);
     }
 
     /**
-     * Tests whether getKeys(String prefix) is correctly synchronized.
+     * Tests whether size() is correctly synchronized.
      */
     @Test
-    public void testGetKeysPrefixSynchronized() {
-        config.getKeys("test");
+    public void testSizeSynchronized() {
+        assertFalse("Wrong size", config.isEmpty());
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
@@ -212,36 +228,20 @@ public class TestAbstractConfigurationSynchronization {
     }
 
     /**
-     * Prepares a mock configuration for a copy operation.
-     *
-     * @return the mock configuration
-     */
-    private static Configuration prepareConfigurationMockForCopy() {
-        final Configuration config2 = EasyMock.createStrictMock(Configuration.class);
-        config2.lock(LockMode.READ);
-        EasyMock.expect(config2.getKeys()).andReturn(Collections.<String>emptySet().iterator());
-        config2.unlock(LockMode.READ);
-        EasyMock.replay(config2);
-        return config2;
-    }
-
-    /**
-     * Tests whether the append() method uses synchronization.
+     * Tests whether a read lock can be released.
      */
     @Test
-    public void testAppendSynchronized() {
-        final Configuration config2 = prepareConfigurationMockForCopy();
-        config.append(config2);
-        EasyMock.verify(config2);
+    public void testUnlockRead() {
+        config.unlock(LockMode.READ);
+        sync.verify(Methods.END_READ);
     }
 
     /**
-     * Tests whether the copy() method uses synchronization.
+     * Tests whether a write lock can be released.
      */
     @Test
-    public void testCopySynchronized() {
-        final Configuration config2 = prepareConfigurationMockForCopy();
-        config.copy(config2);
-        EasyMock.verify(config2);
+    public void testUnlockWrite() {
+        config.unlock(LockMode.WRITE);
+        sync.verify(Methods.END_WRITE);
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestAbstractHierarchicalConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestAbstractHierarchicalConfiguration.java
index facfad5..a6b7db3 100644
--- a/src/test/java/org/apache/commons/configuration2/TestAbstractHierarchicalConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestAbstractHierarchicalConfiguration.java
@@ -55,13 +55,193 @@ import org.junit.Test;
  *
  */
 public class TestAbstractHierarchicalConfiguration {
+    /**
+     * A concrete test implementation of {@code AbstractHierarchicalConfiguration}.
+     */
+    private static class AbstractHierarchicalConfigurationTestImpl extends AbstractHierarchicalConfiguration<ImmutableNode> {
+        public AbstractHierarchicalConfigurationTestImpl(final InMemoryNodeModel model) {
+            super(model);
+        }
+
+        @Override
+        public List<HierarchicalConfiguration<ImmutableNode>> childConfigurationsAt(final String key) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public List<HierarchicalConfiguration<ImmutableNode>> childConfigurationsAt(final String key, final boolean supportUpdates) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        protected NodeModel<ImmutableNode> cloneNodeModel() {
+            return new InMemoryNodeModel(getModel().getNodeHandler().getRootNode());
+        }
+
+        @Override
+        public SubnodeConfiguration configurationAt(final String key) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public SubnodeConfiguration configurationAt(final String key, final boolean supportUpdates) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key, final boolean supportUpdates) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public List<ImmutableHierarchicalConfiguration> immutableChildConfigurationsAt(final String key) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public ImmutableHierarchicalConfiguration immutableConfigurationAt(final String key) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public ImmutableHierarchicalConfiguration immutableConfigurationAt(final String key, final boolean supportUpdates) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+
+        @Override
+        public List<ImmutableHierarchicalConfiguration> immutableConfigurationsAt(final String key) {
+            throw new UnsupportedOperationException("Unexpected method call!");
+        }
+    }
+
+    /**
+     * Checks the content of the passed in configuration object. Used by some tests that copy a configuration.
+     *
+     * @param c the configuration to check
+     */
+    private static void checkContent(final Configuration c) {
+        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
+            assertEquals(NodeStructureHelper.table(i), c.getString("tables.table(" + i + ").name"));
+            for (int j = 0; j < NodeStructureHelper.fieldsLength(i); j++) {
+                assertEquals(NodeStructureHelper.field(i, j), c.getString("tables.table(" + i + ").fields.field(" + j + ").name"));
+            }
+        }
+    }
+
+    private static void checkGetProperty(final AbstractHierarchicalConfiguration<?> testConfig) {
+        assertNull(testConfig.getProperty("tables.table.resultset"));
+        assertNull(testConfig.getProperty("tables.table.fields.field"));
+
+        Object prop = testConfig.getProperty("tables.table(0).fields.field.name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(NodeStructureHelper.fieldsLength(0), ((Collection<?>) prop).size());
+
+        prop = testConfig.getProperty("tables.table.fields.field.name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(totalFieldCount(), ((Collection<?>) prop).size());
+
+        prop = testConfig.getProperty("tables.table.fields.field(3).name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(2, ((Collection<?>) prop).size());
+
+        prop = testConfig.getProperty("tables.table(1).fields.field(2).name");
+        assertNotNull(prop);
+        assertEquals("creationDate", prop.toString());
+    }
+
+    /**
+     * Creates a {@code DefaultConfigurationKey} object.
+     *
+     * @return the new key object
+     */
+    private static DefaultConfigurationKey createConfigurationKey() {
+        return new DefaultConfigurationKey(DefaultExpressionEngine.INSTANCE);
+    }
+
+    /**
+     * Returns the total number of fields in the test data structure.
+     *
+     * @return the total number of fields
+     */
+    private static int totalFieldCount() {
+        int fieldCount = 0;
+        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
+            fieldCount += NodeStructureHelper.fieldsLength(i);
+        }
+        return fieldCount;
+    }
+
     /** The test configuration. */
     private AbstractHierarchicalConfiguration<ImmutableNode> config;
 
-    @Before
-    public void setUp() throws Exception {
-        final ImmutableNode root = new ImmutableNode.Builder(1).addChild(NodeStructureHelper.ROOT_TABLES_TREE).create();
-        config = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel(root));
+    /**
+     * Helper method for checking keys using an alternative syntax.
+     */
+    private void checkAlternativeSyntax() {
+        assertNull(config.getProperty("tables/table/resultset"));
+        assertNull(config.getProperty("tables/table/fields/field"));
+
+        Object prop = config.getProperty("tables/table[0]/fields/field/name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(NodeStructureHelper.fieldsLength(0), ((Collection<?>) prop).size());
+
+        prop = config.getProperty("tables/table/fields/field/name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(totalFieldCount(), ((Collection<?>) prop).size());
+
+        prop = config.getProperty("tables/table/fields/field[3]/name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(2, ((Collection<?>) prop).size());
+
+        prop = config.getProperty("tables/table[1]/fields/field[2]/name");
+        assertNotNull(prop);
+        assertEquals("creationDate", prop.toString());
+
+        final Set<String> keys = ConfigurationAssert.keysToSet(config);
+        assertEquals("Wrong number of defined keys", 2, keys.size());
+        assertTrue("Key not found", keys.contains("tables/table/name"));
+        assertTrue("Key not found", keys.contains("tables/table/fields/field/name"));
+    }
+
+    /**
+     * Helper method for testing the getKeys(String) method.
+     *
+     * @param prefix the key to pass into getKeys()
+     * @param expected the expected result
+     */
+    private void checkKeys(final String prefix, final String[] expected) {
+        final Set<String> values = new HashSet<>();
+        for (final String anExpected : expected) {
+            values.add(anExpected.startsWith(prefix) ? anExpected : prefix + "." + anExpected);
+        }
+
+        final Iterator<String> itKeys = config.getKeys(prefix);
+        while (itKeys.hasNext()) {
+            final String key = itKeys.next();
+            if (!values.contains(key)) {
+                fail("Found unexpected key: " + key);
+            } else {
+                values.remove(key);
+            }
+        }
+
+        assertTrue("Remaining keys " + values, values.isEmpty());
+    }
+
+    private ExpressionEngine createAlternativeExpressionEngine() {
+        return new DefaultExpressionEngine(new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setPropertyDelimiter("/")
+            .setIndexStart("[").setIndexEnd("]").create());
     }
 
     /**
@@ -73,88 +253,126 @@ public class TestAbstractHierarchicalConfiguration {
         return config.getModel().getNodeHandler().getRootNode();
     }
 
+    @Before
+    public void setUp() throws Exception {
+        final ImmutableNode root = new ImmutableNode.Builder(1).addChild(NodeStructureHelper.ROOT_TABLES_TREE).create();
+        config = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel(root));
+    }
+
     @Test
-    public void testIsEmptyFalse() {
-        assertFalse(config.isEmpty());
+    public void testAddNodes() {
+        final Collection<ImmutableNode> nodes = new ArrayList<>();
+        nodes.add(NodeStructureHelper.createFieldNode("birthDate"));
+        nodes.add(NodeStructureHelper.createFieldNode("lastLogin"));
+        nodes.add(NodeStructureHelper.createFieldNode("language"));
+        config.addNodes("tables.table(0).fields", nodes);
+        assertEquals(7, config.getMaxIndex("tables.table(0).fields.field"));
+        assertEquals("birthDate", config.getString("tables.table(0).fields.field(5).name"));
+        assertEquals("lastLogin", config.getString("tables.table(0).fields.field(6).name"));
+        assertEquals("language", config.getString("tables.table(0).fields.field(7).name"));
     }
 
     /**
-     * Tests isEmpty() if only the root node exists.
+     * Tests copying nodes from one configuration to another one.
      */
     @Test
-    public void testIsEmptyRootOnly() {
-        config = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel());
-        assertTrue("Not empty", config.isEmpty());
+    public void testAddNodesCopy() {
+        final AbstractHierarchicalConfigurationTestImpl configDest = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel());
+        configDest.addProperty("test", "TEST");
+        final Collection<ImmutableNode> nodes = getRootNode().getChildren();
+        assertEquals("Wrong number of children", 1, nodes.size());
+        configDest.addNodes("newNodes", nodes);
+        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
+            final String keyTab = "newNodes.tables.table(" + i + ").";
+            assertEquals("Table " + i + " not found", NodeStructureHelper.table(i), configDest.getString(keyTab + "name"));
+            for (int j = 0; j < NodeStructureHelper.fieldsLength(i); j++) {
+                assertEquals("Invalid field " + j + " in table " + i, NodeStructureHelper.field(i, j),
+                    configDest.getString(keyTab + "fields.field(" + j + ").name"));
+            }
+        }
     }
 
     /**
-     * Tests isEmpty() if the structure contains some nodes without values.
+     * Tests the addNodes() method if the provided key does not exist. In this case, a new node (or even a completely new
+     * branch) is created.
      */
     @Test
-    public void testIsEmptyNodesWithNoValues() {
-        final ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder(1);
-        final ImmutableNode.Builder nodeBuilder = new ImmutableNode.Builder(1);
-        nodeBuilder.addChild(NodeStructureHelper.createNode("child", null));
-        rootBuilder.addChild(nodeBuilder.create());
-        config = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel(rootBuilder.create()));
-        assertTrue("Not empty", config.isEmpty());
+    public void testAddNodesForNonExistingKey() {
+        final Collection<ImmutableNode> nodes = new ArrayList<>();
+        final ImmutableNode newNode = new ImmutableNode.Builder().name("usr").value("scott").addAttribute("pwd", "tiger").create();
+        nodes.add(newNode);
+        config.addNodes("database.connection.settings", nodes);
+
+        assertEquals("Usr node not found", "scott", config.getString("database.connection.settings.usr"));
+        assertEquals("Pwd node not found", "tiger", config.getString("database.connection.settings.usr[@pwd]"));
     }
 
-    private static void checkGetProperty(final AbstractHierarchicalConfiguration<?> testConfig) {
-        assertNull(testConfig.getProperty("tables.table.resultset"));
-        assertNull(testConfig.getProperty("tables.table.fields.field"));
+    /**
+     * Tests the addNodes() method when the new nodes should be added to an attribute node. This is not allowed.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddNodesWithAttributeKey() {
+        final Collection<ImmutableNode> nodes = new ArrayList<>();
+        nodes.add(NodeStructureHelper.createNode("testNode", "yes"));
+        config.addNodes("database.connection[@settings]", nodes);
+    }
 
-        Object prop = testConfig.getProperty("tables.table(0).fields.field.name");
+    @Test
+    public void testAddProperty() {
+        config.addProperty("tables.table(0).fields.field(-1).name", "phone");
+        Object prop = config.getProperty("tables.table(0).fields.field.name");
         assertNotNull(prop);
         assertTrue(prop instanceof Collection);
-        assertEquals(NodeStructureHelper.fieldsLength(0), ((Collection<?>) prop).size());
+        assertEquals(6, ((Collection<?>) prop).size());
 
-        prop = testConfig.getProperty("tables.table.fields.field.name");
+        config.addProperty("tables.table(0).fields.field.name", "fax");
+        prop = config.getProperty("tables.table.fields.field(5).name");
         assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(totalFieldCount(), ((Collection<?>) prop).size());
+        assertTrue(prop instanceof List);
+        final List<?> list = (List<?>) prop;
+        assertEquals("phone", list.get(0));
+        assertEquals("fax", list.get(1));
 
-        prop = testConfig.getProperty("tables.table.fields.field(3).name");
+        config.addProperty("tables.table(-1).name", "config");
+        prop = config.getProperty("tables.table.name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(3, ((Collection<?>) prop).size());
+        config.addProperty("tables.table(2).fields.field(0).name", "cid");
+        config.addProperty("tables.table(2).fields.field(-1).name", "confName");
+        prop = config.getProperty("tables.table(2).fields.field.name");
         assertNotNull(prop);
         assertTrue(prop instanceof Collection);
         assertEquals(2, ((Collection<?>) prop).size());
+        assertEquals("confName", config.getProperty("tables.table(2).fields.field(1).name"));
 
-        prop = testConfig.getProperty("tables.table(1).fields.field(2).name");
-        assertNotNull(prop);
-        assertEquals("creationDate", prop.toString());
+        config.addProperty("connection.user", "scott");
+        config.addProperty("connection.passwd", "tiger");
+        assertEquals("tiger", config.getProperty("connection.passwd"));
+
+        final DefaultConfigurationKey key = createConfigurationKey();
+        key.append("tables").append("table").appendIndex(0);
+        key.appendAttribute("tableType");
+        config.addProperty(key.toString(), "system");
+        assertEquals("system", config.getProperty(key.toString()));
     }
 
-    @Test
-    public void testGetProperty() {
-        checkGetProperty(config);
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPropertyInvalidKey() {
+        config.addProperty(".", "InvalidKey");
     }
 
+    /**
+     * Tests whether list handling works correctly when adding properties.
+     */
     @Test
-    public void testSetProperty() {
+    public void testAddPropertyWithListHandling() {
         config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        config.setProperty("tables.table(0).name", "resources");
-        assertEquals("resources", config.getString("tables.table(0).name"));
-        config.setProperty("tables.table.name", "tab1,tab2");
-        assertEquals("tab1", config.getString("tables.table(0).name"));
-        assertEquals("tab2", config.getString("tables.table(1).name"));
-
-        config.setProperty("test.items.item", new int[] {2, 4, 8, 16});
-        assertEquals(3, config.getMaxIndex("test.items.item"));
-        assertEquals(8, config.getInt("test.items.item(2)"));
-        config.setProperty("test.items.item(2)", Integer.valueOf(6));
-        assertEquals(6, config.getInt("test.items.item(2)"));
-        config.setProperty("test.items.item(2)", new int[] {7, 9, 11});
-        assertEquals(5, config.getMaxIndex("test.items.item"));
-
-        config.setProperty("test", Boolean.TRUE);
-        config.setProperty("test.items", "01/01/05");
-        assertEquals(5, config.getMaxIndex("test.items.item"));
-        assertTrue(config.getBoolean("test"));
-        assertEquals("01/01/05", config.getProperty("test.items"));
-
-        config.setProperty("test.items.item", Integer.valueOf(42));
-        assertEquals(0, config.getMaxIndex("test.items.item"));
-        assertEquals(42, config.getInt("test.items.item"));
+        final String key = "list.delimiter.value";
+        config.addProperty(key + ".escaped", "3\\,1415");
+        config.addProperty(key + ".elements", "3,1415");
+        assertEquals("Wrong escaped property", "3,1415", config.getString(key + ".escaped"));
+        assertEquals("Wrong list property", "3", config.getString(key + ".elements"));
     }
 
     @Test
@@ -261,6 +479,40 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     @Test
+    public void testClone() {
+        final Configuration copy = (Configuration) config.clone();
+        assertTrue("Wrong clone result", copy instanceof AbstractHierarchicalConfiguration);
+        checkContent(copy);
+    }
+
+    /**
+     * Tests whether interpolation works as expected after cloning.
+     */
+    @Test
+    public void testCloneInterpolation() {
+        final String keyAnswer = "answer";
+        final String keyValue = "value";
+        config.addProperty(keyAnswer, "The answer is ${" + keyValue + "}.");
+        config.addProperty(keyValue, 42);
+        final Configuration clone = (Configuration) config.clone();
+        clone.setProperty(keyValue, 43);
+        assertEquals("Wrong interpolation in original", "The answer is 42.", config.getString(keyAnswer));
+        assertEquals("Wrong interpolation in clone", "The answer is 43.", clone.getString(keyAnswer));
+    }
+
+    /**
+     * Tests whether registered event handlers are handled correctly when a configuration is cloned. They should not be
+     * registered at the clone.
+     */
+    @Test
+    public void testCloneWithEventListeners() {
+        final EventListener<ConfigurationEvent> l = new EventListenerTestImpl(null);
+        config.addEventListener(ConfigurationEvent.ANY, l);
+        final AbstractHierarchicalConfiguration<?> copy = (AbstractHierarchicalConfiguration<?>) config.clone();
+        assertFalse("Event listener registered at clone", copy.getEventListeners(ConfigurationEvent.ANY).contains(l));
+    }
+
+    @Test
     public void testContainsKey() {
         assertTrue(config.containsKey("tables.table(0).name"));
         assertTrue(config.containsKey("tables.table(1).name"));
@@ -287,21 +539,6 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     /**
-     * Tests whether keys are returned in a defined order.
-     */
-    @Test
-    public void testGetKeysOrder() {
-        config.addProperty("order.key1", "value1");
-        config.addProperty("order.key2", "value2");
-        config.addProperty("order.key3", "value3");
-
-        final Iterator<String> it = config.getKeys("order");
-        assertEquals("1st key", "order.key1", it.next());
-        assertEquals("2nd key", "order.key2", it.next());
-        assertEquals("3rd key", "order.key3", it.next());
-    }
-
-    /**
      * Tests whether attribute keys are contained in the iteration of keys.
      */
     @Test
@@ -325,6 +562,21 @@ public class TestAbstractHierarchicalConfiguration {
         assertFalse("Too many keys", itKeys.hasNext());
     }
 
+    /**
+     * Tests whether keys are returned in a defined order.
+     */
+    @Test
+    public void testGetKeysOrder() {
+        config.addProperty("order.key1", "value1");
+        config.addProperty("order.key2", "value2");
+        config.addProperty("order.key3", "value3");
+
+        final Iterator<String> it = config.getKeys("order");
+        assertEquals("1st key", "order.key1", it.next());
+        assertEquals("2nd key", "order.key2", it.next());
+        assertEquals("3rd key", "order.key3", it.next());
+    }
+
     @Test
     public void testGetKeysString() {
         // add some more properties to make it more interesting
@@ -335,104 +587,42 @@ public class TestAbstractHierarchicalConfiguration {
         config.addProperty("connections.connection.param.url", "url1");
         config.addProperty("connections.connection.param.user", "me");
         config.addProperty("connections.connection.param.pwd", "secret");
-        config.addProperty("connections.connection(-1).param.url", "url2");
-        config.addProperty("connections.connection(1).param.user", "guest");
-
-        checkKeys("tables.table(1)", new String[] {"name", "fields.field.name"});
-        checkKeys("tables.table(0)", new String[] {"name", "fields.field.name", "tables.table(0)[@type]", "size", "fields.field.type", "fields.field.size"});
-        checkKeys("connections.connection(0).param", new String[] {"url", "user", "pwd"});
-        checkKeys("connections.connection(1).param", new String[] {"url", "user"});
-    }
-
-    /**
-     * Tests getKeys() with a prefix when the prefix matches exactly a key.
-     */
-    @Test
-    public void testGetKeysWithKeyAsPrefix() {
-        config.addProperty("order.key1", "value1");
-        config.addProperty("order.key2", "value2");
-        final Iterator<String> it = config.getKeys("order.key1");
-        assertTrue("no key found", it.hasNext());
-        assertEquals("1st key", "order.key1", it.next());
-        assertFalse("more keys than expected", it.hasNext());
-    }
-
-    /**
-     * Tests getKeys() with a prefix when the prefix matches exactly a key, and there are multiple keys starting with this
-     * prefix.
-     */
-    @Test
-    public void testGetKeysWithKeyAsPrefixMultiple() {
-        config.addProperty("order.key1", "value1");
-        config.addProperty("order.key1.test", "value2");
-        config.addProperty("order.key1.test.complex", "value2");
-        final Iterator<String> it = config.getKeys("order.key1");
-        assertEquals("Wrong key 1", "order.key1", it.next());
-        assertEquals("Wrong key 2", "order.key1.test", it.next());
-        assertEquals("Wrong key 3", "order.key1.test.complex", it.next());
-        assertFalse("More keys than expected", it.hasNext());
-    }
-
-    /**
-     * Tests whether the correct size is calculated.
-     */
-    @Test
-    public void testSize() {
-        assertEquals("Wrong size", 2, config.size());
-    }
-
-    @Test
-    public void testAddProperty() {
-        config.addProperty("tables.table(0).fields.field(-1).name", "phone");
-        Object prop = config.getProperty("tables.table(0).fields.field.name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(6, ((Collection<?>) prop).size());
-
-        config.addProperty("tables.table(0).fields.field.name", "fax");
-        prop = config.getProperty("tables.table.fields.field(5).name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof List);
-        final List<?> list = (List<?>) prop;
-        assertEquals("phone", list.get(0));
-        assertEquals("fax", list.get(1));
-
-        config.addProperty("tables.table(-1).name", "config");
-        prop = config.getProperty("tables.table.name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(3, ((Collection<?>) prop).size());
-        config.addProperty("tables.table(2).fields.field(0).name", "cid");
-        config.addProperty("tables.table(2).fields.field(-1).name", "confName");
-        prop = config.getProperty("tables.table(2).fields.field.name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(2, ((Collection<?>) prop).size());
-        assertEquals("confName", config.getProperty("tables.table(2).fields.field(1).name"));
-
-        config.addProperty("connection.user", "scott");
-        config.addProperty("connection.passwd", "tiger");
-        assertEquals("tiger", config.getProperty("connection.passwd"));
+        config.addProperty("connections.connection(-1).param.url", "url2");
+        config.addProperty("connections.connection(1).param.user", "guest");
 
-        final DefaultConfigurationKey key = createConfigurationKey();
-        key.append("tables").append("table").appendIndex(0);
-        key.appendAttribute("tableType");
-        config.addProperty(key.toString(), "system");
-        assertEquals("system", config.getProperty(key.toString()));
+        checkKeys("tables.table(1)", new String[] {"name", "fields.field.name"});
+        checkKeys("tables.table(0)", new String[] {"name", "fields.field.name", "tables.table(0)[@type]", "size", "fields.field.type", "fields.field.size"});
+        checkKeys("connections.connection(0).param", new String[] {"url", "user", "pwd"});
+        checkKeys("connections.connection(1).param", new String[] {"url", "user"});
     }
 
     /**
-     * Creates a {@code DefaultConfigurationKey} object.
-     *
-     * @return the new key object
+     * Tests getKeys() with a prefix when the prefix matches exactly a key.
      */
-    private static DefaultConfigurationKey createConfigurationKey() {
-        return new DefaultConfigurationKey(DefaultExpressionEngine.INSTANCE);
+    @Test
+    public void testGetKeysWithKeyAsPrefix() {
+        config.addProperty("order.key1", "value1");
+        config.addProperty("order.key2", "value2");
+        final Iterator<String> it = config.getKeys("order.key1");
+        assertTrue("no key found", it.hasNext());
+        assertEquals("1st key", "order.key1", it.next());
+        assertFalse("more keys than expected", it.hasNext());
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testAddPropertyInvalidKey() {
-        config.addProperty(".", "InvalidKey");
+    /**
+     * Tests getKeys() with a prefix when the prefix matches exactly a key, and there are multiple keys starting with this
+     * prefix.
+     */
+    @Test
+    public void testGetKeysWithKeyAsPrefixMultiple() {
+        config.addProperty("order.key1", "value1");
+        config.addProperty("order.key1.test", "value2");
+        config.addProperty("order.key1.test.complex", "value2");
+        final Iterator<String> it = config.getKeys("order.key1");
+        assertEquals("Wrong key 1", "order.key1", it.next());
+        assertEquals("Wrong key 2", "order.key1.test", it.next());
+        assertEquals("Wrong key 3", "order.key1.test.complex", it.next());
+        assertFalse("More keys than expected", it.hasNext());
     }
 
     @Test
@@ -453,109 +643,56 @@ public class TestAbstractHierarchicalConfiguration {
         }
     }
 
-    @Test
-    public void testClone() {
-        final Configuration copy = (Configuration) config.clone();
-        assertTrue("Wrong clone result", copy instanceof AbstractHierarchicalConfiguration);
-        checkContent(copy);
-    }
-
     /**
-     * Tests whether registered event handlers are handled correctly when a configuration is cloned. They should not be
-     * registered at the clone.
+     * Tests whether the configuration's node model can be correctly accessed.
      */
     @Test
-    public void testCloneWithEventListeners() {
-        final EventListener<ConfigurationEvent> l = new EventListenerTestImpl(null);
-        config.addEventListener(ConfigurationEvent.ANY, l);
-        final AbstractHierarchicalConfiguration<?> copy = (AbstractHierarchicalConfiguration<?>) config.clone();
-        assertFalse("Event listener registered at clone", copy.getEventListeners(ConfigurationEvent.ANY).contains(l));
-    }
+    public void testGetNodeModel() {
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        config.setSynchronizer(sync);
+        final NodeModel<ImmutableNode> model = config.getNodeModel();
 
-    /**
-     * Tests whether interpolation works as expected after cloning.
-     */
-    @Test
-    public void testCloneInterpolation() {
-        final String keyAnswer = "answer";
-        final String keyValue = "value";
-        config.addProperty(keyAnswer, "The answer is ${" + keyValue + "}.");
-        config.addProperty(keyValue, 42);
-        final Configuration clone = (Configuration) config.clone();
-        clone.setProperty(keyValue, 43);
-        assertEquals("Wrong interpolation in original", "The answer is 42.", config.getString(keyAnswer));
-        assertEquals("Wrong interpolation in clone", "The answer is 43.", clone.getString(keyAnswer));
+        assertTrue("Wrong node model: " + model, model instanceof InMemoryNodeModel);
+        final ImmutableNode rootNode = model.getNodeHandler().getRootNode();
+        assertEquals("Wrong number of children of root node", 1, rootNode.getChildren().size());
+        assertTrue("Wrong children of root node", rootNode.getChildren().contains(NodeStructureHelper.ROOT_TABLES_TREE));
+        sync.verify(SynchronizerTestImpl.Methods.BEGIN_READ, SynchronizerTestImpl.Methods.END_READ);
     }
 
     @Test
-    public void testAddNodes() {
-        final Collection<ImmutableNode> nodes = new ArrayList<>();
-        nodes.add(NodeStructureHelper.createFieldNode("birthDate"));
-        nodes.add(NodeStructureHelper.createFieldNode("lastLogin"));
-        nodes.add(NodeStructureHelper.createFieldNode("language"));
-        config.addNodes("tables.table(0).fields", nodes);
-        assertEquals(7, config.getMaxIndex("tables.table(0).fields.field"));
-        assertEquals("birthDate", config.getString("tables.table(0).fields.field(5).name"));
-        assertEquals("lastLogin", config.getString("tables.table(0).fields.field(6).name"));
-        assertEquals("language", config.getString("tables.table(0).fields.field(7).name"));
+    public void testGetProperty() {
+        checkGetProperty(config);
     }
 
     /**
-     * Tests the addNodes() method if the provided key does not exist. In this case, a new node (or even a completely new
-     * branch) is created.
+     * Tests whether keys that contains brackets can be used.
      */
     @Test
-    public void testAddNodesForNonExistingKey() {
-        final Collection<ImmutableNode> nodes = new ArrayList<>();
-        final ImmutableNode newNode = new ImmutableNode.Builder().name("usr").value("scott").addAttribute("pwd", "tiger").create();
-        nodes.add(newNode);
-        config.addNodes("database.connection.settings", nodes);
-
-        assertEquals("Usr node not found", "scott", config.getString("database.connection.settings.usr"));
-        assertEquals("Pwd node not found", "tiger", config.getString("database.connection.settings.usr[@pwd]"));
-    }
-
-    /**
-     * Tests the addNodes() method when the new nodes should be added to an attribute node. This is not allowed.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testAddNodesWithAttributeKey() {
-        final Collection<ImmutableNode> nodes = new ArrayList<>();
-        nodes.add(NodeStructureHelper.createNode("testNode", "yes"));
-        config.addNodes("database.connection[@settings]", nodes);
+    public void testGetPropertyKeyWithBrackets() {
+        final String key = "test.directory.platform(x86)";
+        config.addProperty(key, "C:\\Temp");
+        assertEquals("Wrong property value", "C:\\Temp", config.getString(key));
     }
 
     /**
-     * Tests copying nodes from one configuration to another one.
+     * Tests the copy constructor when a null reference is passed.
      */
     @Test
-    public void testAddNodesCopy() {
-        final AbstractHierarchicalConfigurationTestImpl configDest = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel());
-        configDest.addProperty("test", "TEST");
-        final Collection<ImmutableNode> nodes = getRootNode().getChildren();
-        assertEquals("Wrong number of children", 1, nodes.size());
-        configDest.addNodes("newNodes", nodes);
-        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
-            final String keyTab = "newNodes.tables.table(" + i + ").";
-            assertEquals("Table " + i + " not found", NodeStructureHelper.table(i), configDest.getString(keyTab + "name"));
-            for (int j = 0; j < NodeStructureHelper.fieldsLength(i); j++) {
-                assertEquals("Invalid field " + j + " in table " + i, NodeStructureHelper.field(i, j),
-                    configDest.getString(keyTab + "fields.field(" + j + ").name"));
-            }
-        }
+    public void testInitCopyNull() {
+        final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration((BaseHierarchicalConfiguration) null);
+        assertTrue("Configuration not empty", copy.isEmpty());
     }
 
     /**
-     * Tests setting a custom expression engine, which uses a slightly different syntax.
+     * Tests obtaining a configuration with all variables substituted.
      */
     @Test
-    public void testSetExpressionEngine() {
-        config.setExpressionEngine(null);
-        assertNotNull("Expression engine is null", config.getExpressionEngine());
-        assertSame("Default engine is not used", DefaultExpressionEngine.INSTANCE, config.getExpressionEngine());
+    public void testInterpolatedConfiguration() {
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        final AbstractHierarchicalConfiguration<?> c = (AbstractHierarchicalConfiguration<?>) InterpolationTestHelper.testInterpolatedConfiguration(config);
 
-        config.setExpressionEngine(createAlternativeExpressionEngine());
-        checkAlternativeSyntax();
+        // tests whether the hierarchical structure has been maintained
+        checkGetProperty(c);
     }
 
     /**
@@ -583,11 +720,27 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     /**
-     * Tests multiple levels of interpolation.
+     * Tests interpolation with constant values.
      */
     @Test
-    public void testInterpolationMultipleLevels() {
-        InterpolationTestHelper.testMultipleInterpolation(config);
+    public void testInterpolationConstants() {
+        InterpolationTestHelper.testInterpolationConstants(config);
+    }
+
+    /**
+     * Tests escaping variables.
+     */
+    @Test
+    public void testInterpolationEscaped() {
+        InterpolationTestHelper.testInterpolationEscaped(config);
+    }
+
+    /**
+     * Tests interpolation with localhost values.
+     */
+    @Test
+    public void testInterpolationLocalhost() {
+        InterpolationTestHelper.testInterpolationLocalhost(config);
     }
 
     /**
@@ -599,6 +752,14 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     /**
+     * Tests multiple levels of interpolation.
+     */
+    @Test
+    public void testInterpolationMultipleLevels() {
+        InterpolationTestHelper.testMultipleInterpolation(config);
+    }
+
+    /**
      * Tests interpolation with a subset.
      */
     @Test
@@ -620,14 +781,6 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     /**
-     * Tests interpolation of a variable, which cannot be resolved.
-     */
-    @Test
-    public void testInterpolationUnknownProperty() {
-        InterpolationTestHelper.testInterpolationUnknownProperty(config);
-    }
-
-    /**
      * Tests interpolation with system properties.
      */
     @Test
@@ -636,113 +789,57 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     /**
-     * Tests interpolation with constant values.
-     */
-    @Test
-    public void testInterpolationConstants() {
-        InterpolationTestHelper.testInterpolationConstants(config);
-    }
-
-    /**
-     * Tests escaping variables.
-     */
-    @Test
-    public void testInterpolationEscaped() {
-        InterpolationTestHelper.testInterpolationEscaped(config);
-    }
-
-    /**
-     * Tests interpolation with localhost values.
+     * Tests interpolation of a variable, which cannot be resolved.
      */
     @Test
-    public void testInterpolationLocalhost() {
-        InterpolationTestHelper.testInterpolationLocalhost(config);
+    public void testInterpolationUnknownProperty() {
+        InterpolationTestHelper.testInterpolationUnknownProperty(config);
     }
 
     /**
      * Tests manipulating the interpolator.
      */
-    @Test
-    public void testInterpolator() {
-        InterpolationTestHelper.testGetInterpolator(config);
-    }
-
-    /**
-     * Tests obtaining a configuration with all variables substituted.
-     */
-    @Test
-    public void testInterpolatedConfiguration() {
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        final AbstractHierarchicalConfiguration<?> c = (AbstractHierarchicalConfiguration<?>) InterpolationTestHelper.testInterpolatedConfiguration(config);
-
-        // tests whether the hierarchical structure has been maintained
-        checkGetProperty(c);
-    }
-
-    /**
-     * Tests the copy constructor when a null reference is passed.
-     */
-    @Test
-    public void testInitCopyNull() {
-        final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration((BaseHierarchicalConfiguration) null);
-        assertTrue("Configuration not empty", copy.isEmpty());
-    }
-
-    /**
-     * Tests whether keys that contains brackets can be used.
-     */
-    @Test
-    public void testGetPropertyKeyWithBrackets() {
-        final String key = "test.directory.platform(x86)";
-        config.addProperty(key, "C:\\Temp");
-        assertEquals("Wrong property value", "C:\\Temp", config.getString(key));
-    }
-
-    /**
-     * Tests whether list handling works correctly when adding properties.
-     */
-    @Test
-    public void testAddPropertyWithListHandling() {
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        final String key = "list.delimiter.value";
-        config.addProperty(key + ".escaped", "3\\,1415");
-        config.addProperty(key + ".elements", "3,1415");
-        assertEquals("Wrong escaped property", "3,1415", config.getString(key + ".escaped"));
-        assertEquals("Wrong list property", "3", config.getString(key + ".elements"));
+    @Test
+    public void testInterpolator() {
+        InterpolationTestHelper.testGetInterpolator(config);
+    }
+
+    @Test
+    public void testIsEmptyFalse() {
+        assertFalse(config.isEmpty());
     }
 
     /**
-     * Tests whether node keys can be resolved.
+     * Tests isEmpty() if the structure contains some nodes without values.
      */
     @Test
-    public void testResolveNodeKey() {
-        final List<ImmutableNode> nodes = config.resolveNodeKey(getRootNode(), "tables.table.name", config.getModel().getNodeHandler());
-        assertEquals("Wrong number of nodes", NodeStructureHelper.tablesLength(), nodes.size());
-        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
-            assertEquals("Wrong node value at " + i, NodeStructureHelper.table(i), nodes.get(i).getValue());
-        }
+    public void testIsEmptyNodesWithNoValues() {
+        final ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder(1);
+        final ImmutableNode.Builder nodeBuilder = new ImmutableNode.Builder(1);
+        nodeBuilder.addChild(NodeStructureHelper.createNode("child", null));
+        rootBuilder.addChild(nodeBuilder.create());
+        config = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel(rootBuilder.create()));
+        assertTrue("Not empty", config.isEmpty());
     }
 
     /**
-     * Tests whether attribute keys are filtered out when resolving node keys.
+     * Tests isEmpty() if only the root node exists.
      */
     @Test
-    public void testResolveNodeKeyAttribute() {
-        final String attrKey = "tables.table(0)[@type]";
-        config.addProperty(attrKey, "system");
-        assertTrue("Got attribute results", config.resolveNodeKey(getRootNode(), attrKey, config.getModel().getNodeHandler()).isEmpty());
+    public void testIsEmptyRootOnly() {
+        config = new AbstractHierarchicalConfigurationTestImpl(new InMemoryNodeModel());
+        assertTrue("Not empty", config.isEmpty());
     }
 
     /**
-     * Tests whether a correct node key is generated if no data is contained in the cache.
+     * Tests nodeKey() if the key is directly found in the cache.
      */
     @Test
-    public void testNodeKeyEmptyCache() {
+    public void testNodeKeyCacheHit() {
         final Map<ImmutableNode, String> cache = new HashMap<>();
-        final ImmutableNode nodeTabName = NodeStructureHelper.nodeForKey(getRootNode(), "tables/table(0)/name");
-        final ImmutableNode nodeFldName = NodeStructureHelper.nodeForKey(getRootNode(), "tables/table(0)/fields/field(1)/name");
-        assertEquals("Wrong key (1)", "tables(0).table(0).name(0)", config.nodeKey(nodeTabName, cache, config.getModel().getNodeHandler()));
-        assertEquals("Wrong key (2)", "tables(0).table(0).fields(0).field(1).name(0)", config.nodeKey(nodeFldName, cache, config.getModel().getNodeHandler()));
+        final String key = "someResultKey";
+        cache.put(getRootNode(), key);
+        assertEquals("Wrong result", key, config.nodeKey(getRootNode(), cache, config.getModel().getNodeHandler()));
     }
 
     /**
@@ -774,191 +871,94 @@ public class TestAbstractHierarchicalConfiguration {
     }
 
     /**
-     * Tests whether a node key for the root node can be generated.
+     * Tests whether a correct node key is generated if no data is contained in the cache.
      */
     @Test
-    public void testNodeKeyRootNode() {
+    public void testNodeKeyEmptyCache() {
         final Map<ImmutableNode, String> cache = new HashMap<>();
-        assertEquals("Wrong root node key", "", config.nodeKey(getRootNode(), cache, config.getModel().getNodeHandler()));
+        final ImmutableNode nodeTabName = NodeStructureHelper.nodeForKey(getRootNode(), "tables/table(0)/name");
+        final ImmutableNode nodeFldName = NodeStructureHelper.nodeForKey(getRootNode(), "tables/table(0)/fields/field(1)/name");
+        assertEquals("Wrong key (1)", "tables(0).table(0).name(0)", config.nodeKey(nodeTabName, cache, config.getModel().getNodeHandler()));
+        assertEquals("Wrong key (2)", "tables(0).table(0).fields(0).field(1).name(0)", config.nodeKey(nodeFldName, cache, config.getModel().getNodeHandler()));
     }
 
     /**
-     * Tests nodeKey() if the key is directly found in the cache.
+     * Tests whether a node key for the root node can be generated.
      */
     @Test
-    public void testNodeKeyCacheHit() {
+    public void testNodeKeyRootNode() {
         final Map<ImmutableNode, String> cache = new HashMap<>();
-        final String key = "someResultKey";
-        cache.put(getRootNode(), key);
-        assertEquals("Wrong result", key, config.nodeKey(getRootNode(), cache, config.getModel().getNodeHandler()));
+        assertEquals("Wrong root node key", "", config.nodeKey(getRootNode(), cache, config.getModel().getNodeHandler()));
     }
 
     /**
-     * Tests whether the configuration's node model can be correctly accessed.
+     * Tests whether node keys can be resolved.
      */
     @Test
-    public void testGetNodeModel() {
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        config.setSynchronizer(sync);
-        final NodeModel<ImmutableNode> model = config.getNodeModel();
-
-        assertTrue("Wrong node model: " + model, model instanceof InMemoryNodeModel);
-        final ImmutableNode rootNode = model.getNodeHandler().getRootNode();
-        assertEquals("Wrong number of children of root node", 1, rootNode.getChildren().size());
-        assertTrue("Wrong children of root node", rootNode.getChildren().contains(NodeStructureHelper.ROOT_TABLES_TREE));
-        sync.verify(SynchronizerTestImpl.Methods.BEGIN_READ, SynchronizerTestImpl.Methods.END_READ);
+    public void testResolveNodeKey() {
+        final List<ImmutableNode> nodes = config.resolveNodeKey(getRootNode(), "tables.table.name", config.getModel().getNodeHandler());
+        assertEquals("Wrong number of nodes", NodeStructureHelper.tablesLength(), nodes.size());
+        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
+            assertEquals("Wrong node value at " + i, NodeStructureHelper.table(i), nodes.get(i).getValue());
+        }
     }
 
     /**
-     * Helper method for testing the getKeys(String) method.
-     *
-     * @param prefix the key to pass into getKeys()
-     * @param expected the expected result
+     * Tests whether attribute keys are filtered out when resolving node keys.
      */
-    private void checkKeys(final String prefix, final String[] expected) {
-        final Set<String> values = new HashSet<>();
-        for (final String anExpected : expected) {
-            values.add(anExpected.startsWith(prefix) ? anExpected : prefix + "." + anExpected);
-        }
-
-        final Iterator<String> itKeys = config.getKeys(prefix);
-        while (itKeys.hasNext()) {
-            final String key = itKeys.next();
-            if (!values.contains(key)) {
-                fail("Found unexpected key: " + key);
-            } else {
-                values.remove(key);
-            }
-        }
-
-        assertTrue("Remaining keys " + values, values.isEmpty());
+    @Test
+    public void testResolveNodeKeyAttribute() {
+        final String attrKey = "tables.table(0)[@type]";
+        config.addProperty(attrKey, "system");
+        assertTrue("Got attribute results", config.resolveNodeKey(getRootNode(), attrKey, config.getModel().getNodeHandler()).isEmpty());
     }
 
     /**
-     * Helper method for checking keys using an alternative syntax.
+     * Tests setting a custom expression engine, which uses a slightly different syntax.
      */
-    private void checkAlternativeSyntax() {
-        assertNull(config.getProperty("tables/table/resultset"));
-        assertNull(config.getProperty("tables/table/fields/field"));
-
-        Object prop = config.getProperty("tables/table[0]/fields/field/name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(NodeStructureHelper.fieldsLength(0), ((Collection<?>) prop).size());
-
-        prop = config.getProperty("tables/table/fields/field/name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(totalFieldCount(), ((Collection<?>) prop).size());
-
-        prop = config.getProperty("tables/table/fields/field[3]/name");
-        assertNotNull(prop);
-        assertTrue(prop instanceof Collection);
-        assertEquals(2, ((Collection<?>) prop).size());
-
-        prop = config.getProperty("tables/table[1]/fields/field[2]/name");
-        assertNotNull(prop);
-        assertEquals("creationDate", prop.toString());
+    @Test
+    public void testSetExpressionEngine() {
+        config.setExpressionEngine(null);
+        assertNotNull("Expression engine is null", config.getExpressionEngine());
+        assertSame("Default engine is not used", DefaultExpressionEngine.INSTANCE, config.getExpressionEngine());
 
-        final Set<String> keys = ConfigurationAssert.keysToSet(config);
-        assertEquals("Wrong number of defined keys", 2, keys.size());
-        assertTrue("Key not found", keys.contains("tables/table/name"));
-        assertTrue("Key not found", keys.contains("tables/table/fields/field/name"));
+        config.setExpressionEngine(createAlternativeExpressionEngine());
+        checkAlternativeSyntax();
     }
 
-    /**
-     * Returns the total number of fields in the test data structure.
-     *
-     * @return the total number of fields
-     */
-    private static int totalFieldCount() {
-        int fieldCount = 0;
-        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
-            fieldCount += NodeStructureHelper.fieldsLength(i);
-        }
-        return fieldCount;
-    }
+    @Test
+    public void testSetProperty() {
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        config.setProperty("tables.table(0).name", "resources");
+        assertEquals("resources", config.getString("tables.table(0).name"));
+        config.setProperty("tables.table.name", "tab1,tab2");
+        assertEquals("tab1", config.getString("tables.table(0).name"));
+        assertEquals("tab2", config.getString("tables.table(1).name"));
 
-    /**
-     * Checks the content of the passed in configuration object. Used by some tests that copy a configuration.
-     *
-     * @param c the configuration to check
-     */
-    private static void checkContent(final Configuration c) {
-        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) {
-            assertEquals(NodeStructureHelper.table(i), c.getString("tables.table(" + i + ").name"));
-            for (int j = 0; j < NodeStructureHelper.fieldsLength(i); j++) {
-                assertEquals(NodeStructureHelper.field(i, j), c.getString("tables.table(" + i + ").fields.field(" + j + ").name"));
-            }
-        }
-    }
+        config.setProperty("test.items.item", new int[] {2, 4, 8, 16});
+        assertEquals(3, config.getMaxIndex("test.items.item"));
+        assertEquals(8, config.getInt("test.items.item(2)"));
+        config.setProperty("test.items.item(2)", Integer.valueOf(6));
+        assertEquals(6, config.getInt("test.items.item(2)"));
+        config.setProperty("test.items.item(2)", new int[] {7, 9, 11});
+        assertEquals(5, config.getMaxIndex("test.items.item"));
 
-    private ExpressionEngine createAlternativeExpressionEngine() {
-        return new DefaultExpressionEngine(new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setPropertyDelimiter("/")
-            .setIndexStart("[").setIndexEnd("]").create());
+        config.setProperty("test", Boolean.TRUE);
+        config.setProperty("test.items", "01/01/05");
+        assertEquals(5, config.getMaxIndex("test.items.item"));
+        assertTrue(config.getBoolean("test"));
+        assertEquals("01/01/05", config.getProperty("test.items"));
+
+        config.setProperty("test.items.item", Integer.valueOf(42));
+        assertEquals(0, config.getMaxIndex("test.items.item"));
+        assertEquals(42, config.getInt("test.items.item"));
     }
 
     /**
-     * A concrete test implementation of {@code AbstractHierarchicalConfiguration}.
+     * Tests whether the correct size is calculated.
      */
-    private static class AbstractHierarchicalConfigurationTestImpl extends AbstractHierarchicalConfiguration<ImmutableNode> {
-        public AbstractHierarchicalConfigurationTestImpl(final InMemoryNodeModel model) {
-            super(model);
-        }
-
-        @Override
-        protected NodeModel<ImmutableNode> cloneNodeModel() {
-            return new InMemoryNodeModel(getModel().getNodeHandler().getRootNode());
-        }
-
-        @Override
-        public SubnodeConfiguration configurationAt(final String key, final boolean supportUpdates) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public SubnodeConfiguration configurationAt(final String key) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key, final boolean supportUpdates) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public List<HierarchicalConfiguration<ImmutableNode>> childConfigurationsAt(final String key) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public List<HierarchicalConfiguration<ImmutableNode>> childConfigurationsAt(final String key, final boolean supportUpdates) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public ImmutableHierarchicalConfiguration immutableConfigurationAt(final String key, final boolean supportUpdates) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public ImmutableHierarchicalConfiguration immutableConfigurationAt(final String key) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public List<ImmutableHierarchicalConfiguration> immutableConfigurationsAt(final String key) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
-
-        @Override
-        public List<ImmutableHierarchicalConfiguration> immutableChildConfigurationsAt(final String key) {
-            throw new UnsupportedOperationException("Unexpected method call!");
-        }
+    @Test
+    public void testSize() {
+        assertEquals("Wrong size", 2, config.size());
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java b/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
index 9d54e9e..803ce9c 100644
--- a/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
+++ b/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
@@ -44,11 +44,60 @@ import org.xml.sax.helpers.DefaultHandler;
  *
  */
 public class TestBaseConfigurationXMLReader {
-    private static final String[] CONTINENTS = {"Africa", "America", "Asia", "Australia", "Europe"};
+    // A ContentHandler that raises an exception
+    private static class TestContentHandler extends DefaultHandler {
+        @Override
+        public void characters(final char[] ch, final int start, final int length) throws SAXException {
+            throw new SAXException("Test exception during parsing");
+        }
+    }
 
+    private static final String[] CONTINENTS = {"Africa", "America", "Asia", "Australia", "Europe"};
     private BaseConfiguration config;
+
     private BaseConfigurationXMLReader configReader;
 
+    private void check(final JXPathContext ctx, final String path, final String value) {
+        check(ctx, path, new String[] {value});
+    }
+
+    /**
+     * Helper method for checking values in the created document.
+     *
+     * @param ctx the JXPath context
+     * @param path the path to be checked
+     * @param values the expected element values
+     */
+    private void check(final JXPathContext ctx, final String path, final String[] values) {
+        final Iterator<?> it = ctx.iterate(path);
+        for (final String value : values) {
+            assertTrue("Too few values", it.hasNext());
+            assertEquals("Wrong property value", value, it.next());
+        }
+        assertFalse("Too many values", it.hasNext());
+    }
+
+    private void checkDocument(final BaseConfigurationXMLReader creader, final String rootName) throws Exception {
+        final SAXSource source = new SAXSource(creader, new InputSource());
+        final DOMResult result = new DOMResult();
+        final Transformer trans = TransformerFactory.newInstance().newTransformer();
+        trans.transform(source, result);
+        final Node root = ((Document) result.getNode()).getDocumentElement();
+        final JXPathContext ctx = JXPathContext.newContext(root);
+
+        assertEquals("Wrong root name", rootName, root.getNodeName());
+        assertEquals("Wrong number of children", 3, ctx.selectNodes("/*").size());
+
+        check(ctx, "world/continents/continent", CONTINENTS);
+        check(ctx, "world/greeting", new String[] {"Hello", "Salute"});
+        check(ctx, "world/wish", "Peace");
+        check(ctx, "application/mail/smtp", "smtp.mymail.org");
+        check(ctx, "application/mail/timeout", "42");
+        check(ctx, "application/mail/account/type", "pop3");
+        check(ctx, "application/mail/account/user", "postmaster");
+        check(ctx, "test", "true");
+    }
+
     @Before
     public void setUp() throws Exception {
         config = new BaseConfiguration();
@@ -72,71 +121,22 @@ public class TestBaseConfigurationXMLReader {
         checkDocument(configReader, "config");
     }
 
-    @Test(expected = SAXException.class)
-    public void testParseSAXException() throws IOException, SAXException {
-        configReader.setContentHandler(new TestContentHandler());
-        configReader.parse("systemID");
-    }
-
     @Test(expected = IOException.class)
     public void testParseIOException() throws SAXException, IOException {
         final BaseConfigurationXMLReader reader = new BaseConfigurationXMLReader();
         reader.parse("document");
     }
 
+    @Test(expected = SAXException.class)
+    public void testParseSAXException() throws IOException, SAXException {
+        configReader.setContentHandler(new TestContentHandler());
+        configReader.parse("systemID");
+    }
+
     @Test
     public void testSetRootName() throws Exception {
         final BaseConfigurationXMLReader reader = new BaseConfigurationXMLReader(config);
         reader.setRootName("apache");
         checkDocument(reader, "apache");
     }
-
-    private void checkDocument(final BaseConfigurationXMLReader creader, final String rootName) throws Exception {
-        final SAXSource source = new SAXSource(creader, new InputSource());
-        final DOMResult result = new DOMResult();
-        final Transformer trans = TransformerFactory.newInstance().newTransformer();
-        trans.transform(source, result);
-        final Node root = ((Document) result.getNode()).getDocumentElement();
-        final JXPathContext ctx = JXPathContext.newContext(root);
-
-        assertEquals("Wrong root name", rootName, root.getNodeName());
-        assertEquals("Wrong number of children", 3, ctx.selectNodes("/*").size());
-
-        check(ctx, "world/continents/continent", CONTINENTS);
-        check(ctx, "world/greeting", new String[] {"Hello", "Salute"});
-        check(ctx, "world/wish", "Peace");
-        check(ctx, "application/mail/smtp", "smtp.mymail.org");
-        check(ctx, "application/mail/timeout", "42");
-        check(ctx, "application/mail/account/type", "pop3");
-        check(ctx, "application/mail/account/user", "postmaster");
-        check(ctx, "test", "true");
-    }
-
-    /**
-     * Helper method for checking values in the created document.
-     *
-     * @param ctx the JXPath context
-     * @param path the path to be checked
-     * @param values the expected element values
-     */
-    private void check(final JXPathContext ctx, final String path, final String[] values) {
-        final Iterator<?> it = ctx.iterate(path);
-        for (final String value : values) {
-            assertTrue("Too few values", it.hasNext());
-            assertEquals("Wrong property value", value, it.next());
-        }
-        assertFalse("Too many values", it.hasNext());
-    }
-
-    private void check(final JXPathContext ctx, final String path, final String value) {
-        check(ctx, path, new String[] {value});
-    }
-
-    // A ContentHandler that raises an exception
-    private static class TestContentHandler extends DefaultHandler {
-        @Override
-        public void characters(final char[] ch, final int start, final int length) throws SAXException {
-            throw new SAXException("Test exception during parsing");
-        }
-    }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestBaseHierarchicalConfigurationSynchronization.java b/src/test/java/org/apache/commons/configuration2/TestBaseHierarchicalConfigurationSynchronization.java
index a1e496d..5448400 100644
--- a/src/test/java/org/apache/commons/configuration2/TestBaseHierarchicalConfigurationSynchronization.java
+++ b/src/test/java/org/apache/commons/configuration2/TestBaseHierarchicalConfigurationSynchronization.java
@@ -47,6 +47,77 @@ import org.junit.Test;
  *
  */
 public class TestBaseHierarchicalConfigurationSynchronization {
+    /**
+     * A thread class for testing concurrent access to SubNode configurations.
+     */
+    private static class SubNodeAccessThread extends Thread {
+        /** The test configuration. */
+        private final HierarchicalConfiguration<ImmutableNode> config;
+
+        /** The latch for synchronizing thread start. */
+        private final CountDownLatch latch;
+
+        /** The key for the sub configuration. */
+        private final String keySub;
+
+        /** The key for the property. */
+        private final String keyProp;
+
+        /** The value read by this thread. */
+        private String value;
+
+        /**
+         * Creates a new instance of {@code SubNodeAccessThread}
+         *
+         * @param c the test configuration
+         * @param startLatch the start latch
+         * @param keySubConfig the key for the sub configuration
+         * @param keyProperty the key for the property
+         */
+        public SubNodeAccessThread(final HierarchicalConfiguration<ImmutableNode> c, final CountDownLatch startLatch, final String keySubConfig,
+            final String keyProperty) {
+            config = c;
+            latch = startLatch;
+            keySub = keySubConfig;
+            keyProp = keyProperty;
+        }
+
+        @Override
+        public void run() {
+            try {
+                latch.await();
+                final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt(keySub, true);
+                value = subConfig.getString(keyProp);
+            } catch (final InterruptedException iex) {
+                // ignore
+            }
+        }
+
+        /**
+         * Verifies that the correct value was read.
+         */
+        public void verify() {
+            try {
+                join();
+            } catch (final InterruptedException e) {
+                fail("Waiting was interrupted: " + e);
+            }
+            assertEquals("Wrong value", "I'm complex!", value);
+        }
+    }
+
+    /**
+     * Tests whether the specified configuration is detached.
+     *
+     * @param c the configuration to test
+     * @return a flag whether the root node of this configuration is detached
+     */
+    private static boolean isDetached(final HierarchicalConfiguration<ImmutableNode> c) {
+        assertTrue("Not a sub configuration", c instanceof SubnodeConfiguration);
+        final InMemoryNodeModel nodeModel = ((SubnodeConfiguration) c).getRootNodeModel();
+        return nodeModel.isTrackedNodeDetached(((SubnodeConfiguration) c).getRootSelector());
+    }
+
     /** The test synchronizer. */
     private SynchronizerTestImpl sync;
 
@@ -67,60 +138,59 @@ public class TestBaseHierarchicalConfigurationSynchronization {
     }
 
     /**
-     * Tests whether getMaxIndex() is correctly synchronized.
-     */
-    @Test
-    public void testGetMaxIndexSynchronized() {
-        assertTrue("Wrong max index", config.getMaxIndex("list.item") > 0);
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-    }
-
-    /**
-     * Tests whether getRootElementName() is correctly synchronized.
+     * Tests whether addNodes() is correctly synchronized.
      */
     @Test
-    public void testGetRootElementNameSynchronized() {
-        assertEquals("Wrong root element name", "testconfig", config.getRootElementName());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+    public void testAddNodesSynchronized() {
+        final ImmutableNode node = NodeStructureHelper.createNode("newNode", "true");
+        config.addNodes("test.addNodes", Collections.singleton(node));
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     /**
-     * Tests whether clone() is correctly synchronized.
+     * Tests whether childConfigurationsAt() is correctly synchronized.
      */
     @Test
-    public void testCloneSynchronized() {
-        final BaseHierarchicalConfiguration clone = (BaseHierarchicalConfiguration) config.clone();
+    public void testChildConfigurationsAtSynchronized() {
+        final List<HierarchicalConfiguration<ImmutableNode>> subs = config.childConfigurationsAt("clear");
+        assertFalse("No subnode configurations", subs.isEmpty());
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        assertNotSame("Synchronizer was not cloned", config.getSynchronizer(), clone.getSynchronizer());
     }
 
     /**
-     * Tests whether addNodes() is correctly synchronized.
+     * Tests whether clearTree() is correctly synchronized.
      */
     @Test
-    public void testAddNodesSynchronized() {
-        final ImmutableNode node = NodeStructureHelper.createNode("newNode", "true");
-        config.addNodes("test.addNodes", Collections.singleton(node));
+    public void testClearTreeSynchronized() {
+        config.clearTree("clear");
         sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     /**
-     * Tests whether clearTree() is correctly synchronized.
+     * Tests whether a clone() operation also copies the data used to manage SubnodeConfiguration objects.
      */
     @Test
-    public void testClearTreeSynchronized() {
-        config.clearTree("clear");
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+    public void testCloneCopySubnodeData() {
+        final BaseHierarchicalConfiguration conf2 = new BaseHierarchicalConfiguration(config);
+
+        final HierarchicalConfiguration<ImmutableNode> sub = conf2.configurationAt("element2.subelement", true);
+        @SuppressWarnings("unchecked") // clone retains the type
+        final HierarchicalConfiguration<ImmutableNode> copy = (HierarchicalConfiguration<ImmutableNode>) conf2.clone();
+        final HierarchicalConfiguration<ImmutableNode> sub2 = copy.configurationAt("element2.subelement", true);
+        // This must not cause a validate operation on sub1, but on sub2
+        copy.clearTree("element2");
+        assertTrue("Sub2 not detached", isDetached(sub2));
+        assertFalse("Sub 1 was detached", isDetached(sub));
     }
 
     /**
-     * Tests whether synchronization is performed when copying a configuration.
+     * Tests whether clone() is correctly synchronized.
      */
     @Test
-    public void testCopyConstructorSynchronized() {
-        final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration(config);
+    public void testCloneSynchronized() {
+        final BaseHierarchicalConfiguration clone = (BaseHierarchicalConfiguration) config.clone();
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        assertNotSame("Synchronizer was copied", sync, copy.getSynchronizer());
+        assertNotSame("Synchronizer was not cloned", config.getSynchronizer(), clone.getSynchronizer());
     }
 
     /**
@@ -144,78 +214,31 @@ public class TestBaseHierarchicalConfigurationSynchronization {
     }
 
     /**
-     * Tests whether childConfigurationsAt() is correctly synchronized.
+     * Tests whether synchronization is performed when copying a configuration.
      */
     @Test
-    public void testChildConfigurationsAtSynchronized() {
-        final List<HierarchicalConfiguration<ImmutableNode>> subs = config.childConfigurationsAt("clear");
-        assertFalse("No subnode configurations", subs.isEmpty());
+    public void testCopyConstructorSynchronized() {
+        final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration(config);
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+        assertNotSame("Synchronizer was copied", sync, copy.getSynchronizer());
     }
 
     /**
-     * Tests whether the specified configuration is detached.
-     *
-     * @param c the configuration to test
-     * @return a flag whether the root node of this configuration is detached
-     */
-    private static boolean isDetached(final HierarchicalConfiguration<ImmutableNode> c) {
-        assertTrue("Not a sub configuration", c instanceof SubnodeConfiguration);
-        final InMemoryNodeModel nodeModel = ((SubnodeConfiguration) c).getRootNodeModel();
-        return nodeModel.isTrackedNodeDetached(((SubnodeConfiguration) c).getRootSelector());
-    }
-
-    /**
-     * Tests whether updates on nodes are communicated to all SubnodeConfigurations of a configuration.
-     */
-    @Test
-    public void testSubnodeUpdate() {
-        config.addProperty("element2.test", Boolean.TRUE);
-        final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2", true);
-        final HierarchicalConfiguration<ImmutableNode> subsub = sub.configurationAt("subelement", true);
-        config.clearTree("element2.subelement");
-        assertFalse("Sub1 detached", isDetached(sub));
-        assertTrue("Sub2 still attached", isDetached(subsub));
-    }
-
-    /**
-     * Tests whether updates caused by a SubnodeConfiguration are communicated to all other SubnodeConfigurations.
-     */
-    @Test
-    public void testSubnodeUpdateBySubnode() {
-        final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2", true);
-        final HierarchicalConfiguration<ImmutableNode> subsub = sub.configurationAt("subelement", true);
-        final HierarchicalConfiguration<ImmutableNode> sub2 = config.configurationAt("element2.subelement", true);
-        sub.clearTree("subelement");
-        assertTrue("Sub2 still attached", isDetached(sub2));
-        assertTrue("Subsub still attached", isDetached(subsub));
-    }
-
-    /**
-     * Tests whether a clone() operation also copies the data used to manage SubnodeConfiguration objects.
+     * Tests whether getMaxIndex() is correctly synchronized.
      */
     @Test
-    public void testCloneCopySubnodeData() {
-        final BaseHierarchicalConfiguration conf2 = new BaseHierarchicalConfiguration(config);
-
-        final HierarchicalConfiguration<ImmutableNode> sub = conf2.configurationAt("element2.subelement", true);
-        @SuppressWarnings("unchecked") // clone retains the type
-        final HierarchicalConfiguration<ImmutableNode> copy = (HierarchicalConfiguration<ImmutableNode>) conf2.clone();
-        final HierarchicalConfiguration<ImmutableNode> sub2 = copy.configurationAt("element2.subelement", true);
-        // This must not cause a validate operation on sub1, but on sub2
-        copy.clearTree("element2");
-        assertTrue("Sub2 not detached", isDetached(sub2));
-        assertFalse("Sub 1 was detached", isDetached(sub));
+    public void testGetMaxIndexSynchronized() {
+        assertTrue("Wrong max index", config.getMaxIndex("list.item") > 0);
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
-     * Tests whether subset() is correctly synchronized.
+     * Tests whether getRootElementName() is correctly synchronized.
      */
     @Test
-    public void testSubsetSynchronized() {
-        final Configuration subset = config.subset("test");
+    public void testGetRootElementNameSynchronized() {
+        assertEquals("Wrong root element name", "testconfig", config.getRootElementName());
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        assertSame("Wrong Synchronizer", sync, subset.getSynchronizer());
     }
 
     /**
@@ -247,61 +270,38 @@ public class TestBaseHierarchicalConfigurationSynchronization {
     }
 
     /**
-     * A thread class for testing concurrent access to SubNode configurations.
+     * Tests whether updates on nodes are communicated to all SubnodeConfigurations of a configuration.
      */
-    private static class SubNodeAccessThread extends Thread {
-        /** The test configuration. */
-        private final HierarchicalConfiguration<ImmutableNode> config;
-
-        /** The latch for synchronizing thread start. */
-        private final CountDownLatch latch;
-
-        /** The key for the sub configuration. */
-        private final String keySub;
-
-        /** The key for the property. */
-        private final String keyProp;
-
-        /** The value read by this thread. */
-        private String value;
-
-        /**
-         * Creates a new instance of {@code SubNodeAccessThread}
-         *
-         * @param c the test configuration
-         * @param startLatch the start latch
-         * @param keySubConfig the key for the sub configuration
-         * @param keyProperty the key for the property
-         */
-        public SubNodeAccessThread(final HierarchicalConfiguration<ImmutableNode> c, final CountDownLatch startLatch, final String keySubConfig,
-            final String keyProperty) {
-            config = c;
-            latch = startLatch;
-            keySub = keySubConfig;
-            keyProp = keyProperty;
-        }
+    @Test
+    public void testSubnodeUpdate() {
+        config.addProperty("element2.test", Boolean.TRUE);
+        final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2", true);
+        final HierarchicalConfiguration<ImmutableNode> subsub = sub.configurationAt("subelement", true);
+        config.clearTree("element2.subelement");
+        assertFalse("Sub1 detached", isDetached(sub));
+        assertTrue("Sub2 still attached", isDetached(subsub));
+    }
 
-        @Override
-        public void run() {
-            try {
-                latch.await();
-                final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt(keySub, true);
-                value = subConfig.getString(keyProp);
-            } catch (final InterruptedException iex) {
-                // ignore
-            }
-        }
+    /**
+     * Tests whether updates caused by a SubnodeConfiguration are communicated to all other SubnodeConfigurations.
+     */
+    @Test
+    public void testSubnodeUpdateBySubnode() {
+        final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2", true);
+        final HierarchicalConfiguration<ImmutableNode> subsub = sub.configurationAt("subelement", true);
+        final HierarchicalConfiguration<ImmutableNode> sub2 = config.configurationAt("element2.subelement", true);
+        sub.clearTree("subelement");
+        assertTrue("Sub2 still attached", isDetached(sub2));
+        assertTrue("Subsub still attached", isDetached(subsub));
+    }
 
-        /**
-         * Verifies that the correct value was read.
-         */
-        public void verify() {
-            try {
-                join();
-            } catch (final InterruptedException e) {
-                fail("Waiting was interrupted: " + e);
-            }
-            assertEquals("Wrong value", "I'm complex!", value);
-        }
+    /**
+     * Tests whether subset() is correctly synchronized.
+     */
+    @Test
+    public void testSubsetSynchronized() {
+        final Configuration subset = config.subset("test");
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+        assertSame("Wrong Synchronizer", sync, subset.getSynchronizer());
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestBaseNullConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestBaseNullConfiguration.java
index 49f355d..3ff097d 100644
--- a/src/test/java/org/apache/commons/configuration2/TestBaseNullConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestBaseNullConfiguration.java
@@ -50,107 +50,107 @@ public class TestBaseNullConfiguration {
     }
 
     @Test
-    public void testThrowExceptionOnMissing() {
-        assertFalse("Throw Exception Property is set!", config.isThrowExceptionOnMissing());
+    public void testCommaSeparatedString() {
+        final String prop = "hey, that's a test";
+        config.setProperty("prop.string", prop);
+        final List<Object> list = config.getList("prop.string");
+        assertEquals("Wrong number of elements", 2, list.size());
+        assertEquals("Wrong element 1", "hey", list.get(0));
     }
 
     @Test
-    public void testGetProperty() {
-        /* should be empty and return null */
-        assertEquals("This returns null", config.getProperty("foo"), null);
-
-        /* add a real value, and get it two different ways */
-        config.setProperty("number", "1");
-        assertEquals("This returns '1'", config.getProperty("number"), "1");
-        assertEquals("This returns '1'", config.getString("number"), "1");
+    public void testCommaSeparatedStringEscaped() {
+        final String prop2 = "hey\\, that's a test";
+        config.clearProperty("prop.string");
+        config.setProperty("prop.string", prop2);
+        assertEquals("Wrong value", "hey, that's a test", config.getString("prop.string"));
     }
 
     @Test
-    public void testGetByte() {
-        config.setProperty("number", "1");
-        final byte oneB = 1;
-        final byte twoB = 2;
-        assertEquals("This returns 1(byte)", oneB, config.getByte("number"));
-        assertEquals("This returns 1(byte)", oneB, config.getByte("number", twoB));
-        assertEquals("This returns 2(default byte)", twoB, config.getByte("numberNotInConfig", twoB));
-        assertEquals("This returns 1(Byte)", Byte.valueOf(oneB), config.getByte("number", Byte.valueOf("2")));
-    }
+    public void testGetBigDecimal() {
+        config.setProperty("numberBigD", "123.456");
+        final BigDecimal number = new BigDecimal("123.456");
+        final BigDecimal defaultValue = new BigDecimal("654.321");
 
-    @Test(expected = NoSuchElementException.class)
-    public void testGetByteUnknown() {
-        config.getByte("numberNotInConfig");
+        assertEquals("Existing key", number, config.getBigDecimal("numberBigD"));
+        assertEquals("Existing key with default value", number, config.getBigDecimal("numberBigD", defaultValue));
+        assertEquals("Missing key with default value", defaultValue, config.getBigDecimal("numberNotInConfig", defaultValue));
     }
 
     @Test(expected = ConversionException.class)
-    public void testGetByteIncompatibleType() {
+    public void testGetBigDecimalIncompatibleType() {
         config.setProperty("test.empty", "");
-        config.getByte("test.empty");
+        config.getBigDecimal("test.empty");
     }
 
     @Test
-    public void testGetShort() {
-        config.setProperty("numberS", "1");
-        final short oneS = 1;
-        final short twoS = 2;
-        assertEquals("This returns 1(short)", oneS, config.getShort("numberS"));
-        assertEquals("This returns 1(short)", oneS, config.getShort("numberS", twoS));
-        assertEquals("This returns 2(default short)", twoS, config.getShort("numberNotInConfig", twoS));
-        assertEquals("This returns 1(Short)", Short.valueOf(oneS), config.getShort("numberS", Short.valueOf("2")));
+    public void testGetBigDecimalUnknown() {
+        assertNull("Missing Key is not null!", config.getBigDecimal("numberNotInConfig"));
     }
 
-    @Test(expected = NoSuchElementException.class)
-    public void testGetShortUnknown() {
-        config.getShort("numberNotInConfig");
+    @Test
+    public void testGetBigInteger() {
+        config.setProperty("numberBigI", "1234567890");
+        final BigInteger number = new BigInteger("1234567890");
+        final BigInteger defaultValue = new BigInteger("654321");
+
+        assertEquals("Existing key", number, config.getBigInteger("numberBigI"));
+        assertEquals("Existing key with default value", number, config.getBigInteger("numberBigI", defaultValue));
+        assertEquals("Missing key with default value", defaultValue, config.getBigInteger("numberNotInConfig", defaultValue));
     }
 
     @Test(expected = ConversionException.class)
-    public void testGetShortIncompatibleType() {
+    public void testGetBigIntegerIncompatibleType() {
         config.setProperty("test.empty", "");
-        config.getShort("test.empty");
+        config.getBigInteger("test.empty");
     }
 
     @Test
-    public void testGetLong() {
-        config.setProperty("numberL", "1");
-        final long oneL = 1;
-        final long twoL = 2;
-        assertEquals("This returns 1(long)", oneL, config.getLong("numberL"));
-        assertEquals("This returns 1(long)", oneL, config.getLong("numberL", twoL));
-        assertEquals("This returns 2(default long)", twoL, config.getLong("numberNotInConfig", twoL));
-        assertEquals("This returns 1(Long)", Long.valueOf(oneL), config.getLong("numberL", Long.valueOf("2")));
+    public void testGetBigIntegerUnknown() {
+        assertNull("Missing Key is not null!", config.getBigInteger("numberNotInConfig"));
     }
 
-    @Test(expected = NoSuchElementException.class)
-    public void testGetLongUnknown() {
-        config.getLong("numberNotInConfig");
+    @Test
+    public void testGetBoolean() {
+        config.setProperty("boolA", Boolean.TRUE);
+        final boolean boolT = true, boolF = false;
+        assertEquals("This returns true", boolT, config.getBoolean("boolA"));
+        assertEquals("This returns true, not the default", boolT, config.getBoolean("boolA", boolF));
+        assertEquals("This returns false(default)", boolF, config.getBoolean("boolNotInConfig", boolF));
+        assertEquals("This returns true(Boolean)", Boolean.valueOf(boolT), config.getBoolean("boolA", Boolean.valueOf(boolF)));
     }
 
     @Test(expected = ConversionException.class)
-    public void testGetLongIncompatibleTypes() {
+    public void testGetBooleanIncompatibleType() {
         config.setProperty("test.empty", "");
-        config.getLong("test.empty");
+        config.getBoolean("test.empty");
     }
 
-    @Test
-    public void testGetFloat() {
-        config.setProperty("numberF", "1.0");
-        final float oneF = 1;
-        final float twoF = 2;
-        assertEquals("This returns 1(float)", oneF, config.getFloat("numberF"), 0);
-        assertEquals("This returns 1(float)", oneF, config.getFloat("numberF", twoF), 0);
-        assertEquals("This returns 2(default float)", twoF, config.getFloat("numberNotInConfig", twoF), 0);
-        assertEquals("This returns 1(Float)", Float.valueOf(oneF), config.getFloat("numberF", Float.valueOf("2")));
+    @Test(expected = NoSuchElementException.class)
+    public void testGetBooleanUnknown() {
+        config.getBoolean("numberNotInConfig");
     }
 
-    @Test(expected = NoSuchElementException.class)
-    public void testGetFloatUnknown() {
-        config.getFloat("numberNotInConfig");
+    @Test
+    public void testGetByte() {
+        config.setProperty("number", "1");
+        final byte oneB = 1;
+        final byte twoB = 2;
+        assertEquals("This returns 1(byte)", oneB, config.getByte("number"));
+        assertEquals("This returns 1(byte)", oneB, config.getByte("number", twoB));
+        assertEquals("This returns 2(default byte)", twoB, config.getByte("numberNotInConfig", twoB));
+        assertEquals("This returns 1(Byte)", Byte.valueOf(oneB), config.getByte("number", Byte.valueOf("2")));
     }
 
     @Test(expected = ConversionException.class)
-    public void testGetFloatIncompatibleType() {
+    public void testGetByteIncompatibleType() {
         config.setProperty("test.empty", "");
-        config.getFloat("test.empty");
+        config.getByte("test.empty");
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void testGetByteUnknown() {
+        config.getByte("numberNotInConfig");
     }
 
     @Test
@@ -164,59 +164,110 @@ public class TestBaseNullConfiguration {
         assertEquals("This returns 1(Double)", Double.valueOf(oneD), config.getDouble("numberD", Double.valueOf("2")));
     }
 
+    @Test(expected = ConversionException.class)
+    public void testGetDoubleIncompatibleType() {
+        config.setProperty("test.empty", "");
+        config.getDouble("test.empty");
+    }
+
     @Test(expected = NoSuchElementException.class)
     public void testGetDoubleUnknown() {
         config.getDouble("numberNotInConfig");
     }
 
+    @Test
+    public void testGetFloat() {
+        config.setProperty("numberF", "1.0");
+        final float oneF = 1;
+        final float twoF = 2;
+        assertEquals("This returns 1(float)", oneF, config.getFloat("numberF"), 0);
+        assertEquals("This returns 1(float)", oneF, config.getFloat("numberF", twoF), 0);
+        assertEquals("This returns 2(default float)", twoF, config.getFloat("numberNotInConfig", twoF), 0);
+        assertEquals("This returns 1(Float)", Float.valueOf(oneF), config.getFloat("numberF", Float.valueOf("2")));
+    }
+
     @Test(expected = ConversionException.class)
-    public void testGetDoubleIncompatibleType() {
+    public void testGetFloatIncompatibleType() {
         config.setProperty("test.empty", "");
-        config.getDouble("test.empty");
+        config.getFloat("test.empty");
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void testGetFloatUnknown() {
+        config.getFloat("numberNotInConfig");
     }
 
     @Test
-    public void testGetBigDecimal() {
-        config.setProperty("numberBigD", "123.456");
-        final BigDecimal number = new BigDecimal("123.456");
-        final BigDecimal defaultValue = new BigDecimal("654.321");
+    public void testGetList() {
+        config.addProperty("number", "1");
+        config.addProperty("number", "2");
+        final List<Object> list = config.getList("number");
+        assertNotNull("The list is null", list);
+        assertEquals("List size", 2, list.size());
+        assertTrue("The number 1 is missing from the list", list.contains("1"));
+        assertTrue("The number 2 is missing from the list", list.contains("2"));
+    }
 
-        assertEquals("Existing key", number, config.getBigDecimal("numberBigD"));
-        assertEquals("Existing key with default value", number, config.getBigDecimal("numberBigD", defaultValue));
-        assertEquals("Missing key with default value", defaultValue, config.getBigDecimal("numberNotInConfig", defaultValue));
+    @Test
+    public void testGetListAsScalar() {
+        config.addProperty("number", "1");
+        config.addProperty("number", "2");
+        assertEquals("Wrong value", "1", config.getString("number"));
     }
 
     @Test
-    public void testGetBigDecimalUnknown() {
-        assertNull("Missing Key is not null!", config.getBigDecimal("numberNotInConfig"));
+    public void testGetLong() {
+        config.setProperty("numberL", "1");
+        final long oneL = 1;
+        final long twoL = 2;
+        assertEquals("This returns 1(long)", oneL, config.getLong("numberL"));
+        assertEquals("This returns 1(long)", oneL, config.getLong("numberL", twoL));
+        assertEquals("This returns 2(default long)", twoL, config.getLong("numberNotInConfig", twoL));
+        assertEquals("This returns 1(Long)", Long.valueOf(oneL), config.getLong("numberL", Long.valueOf("2")));
     }
 
     @Test(expected = ConversionException.class)
-    public void testGetBigDecimalIncompatibleType() {
+    public void testGetLongIncompatibleTypes() {
         config.setProperty("test.empty", "");
-        config.getBigDecimal("test.empty");
+        config.getLong("test.empty");
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void testGetLongUnknown() {
+        config.getLong("numberNotInConfig");
     }
 
     @Test
-    public void testGetBigInteger() {
-        config.setProperty("numberBigI", "1234567890");
-        final BigInteger number = new BigInteger("1234567890");
-        final BigInteger defaultValue = new BigInteger("654321");
+    public void testGetProperty() {
+        /* should be empty and return null */
+        assertEquals("This returns null", config.getProperty("foo"), null);
 
-        assertEquals("Existing key", number, config.getBigInteger("numberBigI"));
-        assertEquals("Existing key with default value", number, config.getBigInteger("numberBigI", defaultValue));
-        assertEquals("Missing key with default value", defaultValue, config.getBigInteger("numberNotInConfig", defaultValue));
+        /* add a real value, and get it two different ways */
+        config.setProperty("number", "1");
+        assertEquals("This returns '1'", config.getProperty("number"), "1");
+        assertEquals("This returns '1'", config.getString("number"), "1");
     }
 
     @Test
-    public void testGetBigIntegerUnknown() {
-        assertNull("Missing Key is not null!", config.getBigInteger("numberNotInConfig"));
+    public void testGetShort() {
+        config.setProperty("numberS", "1");
+        final short oneS = 1;
+        final short twoS = 2;
+        assertEquals("This returns 1(short)", oneS, config.getShort("numberS"));
+        assertEquals("This returns 1(short)", oneS, config.getShort("numberS", twoS));
+        assertEquals("This returns 2(default short)", twoS, config.getShort("numberNotInConfig", twoS));
+        assertEquals("This returns 1(Short)", Short.valueOf(oneS), config.getShort("numberS", Short.valueOf("2")));
     }
 
     @Test(expected = ConversionException.class)
-    public void testGetBigIntegerIncompatibleType() {
+    public void testGetShortIncompatibleType() {
         config.setProperty("test.empty", "");
-        config.getBigInteger("test.empty");
+        config.getShort("test.empty");
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void testGetShortUnknown() {
+        config.getShort("numberNotInConfig");
     }
 
     @Test
@@ -236,59 +287,41 @@ public class TestBaseNullConfiguration {
     }
 
     @Test
-    public void testGetBoolean() {
-        config.setProperty("boolA", Boolean.TRUE);
-        final boolean boolT = true, boolF = false;
-        assertEquals("This returns true", boolT, config.getBoolean("boolA"));
-        assertEquals("This returns true, not the default", boolT, config.getBoolean("boolA", boolF));
-        assertEquals("This returns false(default)", boolF, config.getBoolean("boolNotInConfig", boolF));
-        assertEquals("This returns true(Boolean)", Boolean.valueOf(boolT), config.getBoolean("boolA", Boolean.valueOf(boolF)));
-    }
+    public void testInterpolation() throws Exception {
+        config.setProperty("applicationRoot", "/home/applicationRoot");
+        config.setProperty("db", "${applicationRoot}/db/hypersonic");
+        final String unInterpolatedValue = "${applicationRoot2}/db/hypersonic";
+        config.setProperty("dbFailedInterpolate", unInterpolatedValue);
+        final String dbProp = "/home/applicationRoot/db/hypersonic";
 
-    @Test(expected = NoSuchElementException.class)
-    public void testGetBooleanUnknown() {
-        config.getBoolean("numberNotInConfig");
-    }
+        // construct a new config, using config as the defaults config for it.
+        final BaseConfiguration superProp = config;
 
-    @Test(expected = ConversionException.class)
-    public void testGetBooleanIncompatibleType() {
-        config.setProperty("test.empty", "");
-        config.getBoolean("test.empty");
-    }
+        assertEquals("Checking interpolated variable", dbProp, superProp.getString("db"));
+        assertEquals("lookup fails, leave variable as is", superProp.getString("dbFailedInterpolate"), unInterpolatedValue);
 
-    @Test
-    public void testGetList() {
-        config.addProperty("number", "1");
-        config.addProperty("number", "2");
-        final List<Object> list = config.getList("number");
-        assertNotNull("The list is null", list);
-        assertEquals("List size", 2, list.size());
-        assertTrue("The number 1 is missing from the list", list.contains("1"));
-        assertTrue("The number 2 is missing from the list", list.contains("2"));
+        superProp.setProperty("arrayInt", "${applicationRoot}/1");
+        final String[] arrayInt = superProp.getStringArray("arrayInt");
+        assertEquals("check first entry was interpolated", "/home/applicationRoot/1", arrayInt[0]);
     }
 
-    @Test
-    public void testGetListAsScalar() {
-        config.addProperty("number", "1");
-        config.addProperty("number", "2");
-        assertEquals("Wrong value", "1", config.getString("number"));
+    @Test(expected = IllegalStateException.class)
+    public void testInterpolationLoop() throws Exception {
+        config.setProperty("test.a", "${test.b}");
+        config.setProperty("test.b", "${test.a}");
+        config.getString("test.a");
     }
 
     @Test
-    public void testCommaSeparatedString() {
-        final String prop = "hey, that's a test";
-        config.setProperty("prop.string", prop);
-        final List<Object> list = config.getList("prop.string");
-        assertEquals("Wrong number of elements", 2, list.size());
-        assertEquals("Wrong element 1", "hey", list.get(0));
-    }
+    public void testMultipleInterpolation() throws Exception {
+        config.setProperty("test.base-level", "/base-level");
+        config.setProperty("test.first-level", "${test.base-level}/first-level");
+        config.setProperty("test.second-level", "${test.first-level}/second-level");
+        config.setProperty("test.third-level", "${test.second-level}/third-level");
 
-    @Test
-    public void testCommaSeparatedStringEscaped() {
-        final String prop2 = "hey\\, that's a test";
-        config.clearProperty("prop.string");
-        config.setProperty("prop.string", prop2);
-        assertEquals("Wrong value", "hey, that's a test", config.getString("prop.string"));
+        final String expectedValue = "/base-level/first-level/second-level/third-level";
+
+        assertEquals(config.getString("test.third-level"), expectedValue);
     }
 
     @Test
@@ -332,40 +365,7 @@ public class TestBaseNullConfiguration {
     }
 
     @Test
-    public void testInterpolation() throws Exception {
-        config.setProperty("applicationRoot", "/home/applicationRoot");
-        config.setProperty("db", "${applicationRoot}/db/hypersonic");
-        final String unInterpolatedValue = "${applicationRoot2}/db/hypersonic";
-        config.setProperty("dbFailedInterpolate", unInterpolatedValue);
-        final String dbProp = "/home/applicationRoot/db/hypersonic";
-
-        // construct a new config, using config as the defaults config for it.
-        final BaseConfiguration superProp = config;
-
-        assertEquals("Checking interpolated variable", dbProp, superProp.getString("db"));
-        assertEquals("lookup fails, leave variable as is", superProp.getString("dbFailedInterpolate"), unInterpolatedValue);
-
-        superProp.setProperty("arrayInt", "${applicationRoot}/1");
-        final String[] arrayInt = superProp.getStringArray("arrayInt");
-        assertEquals("check first entry was interpolated", "/home/applicationRoot/1", arrayInt[0]);
-    }
-
-    @Test
-    public void testMultipleInterpolation() throws Exception {
-        config.setProperty("test.base-level", "/base-level");
-        config.setProperty("test.first-level", "${test.base-level}/first-level");
-        config.setProperty("test.second-level", "${test.first-level}/second-level");
-        config.setProperty("test.third-level", "${test.second-level}/third-level");
-
-        final String expectedValue = "/base-level/first-level/second-level/third-level";
-
-        assertEquals(config.getString("test.third-level"), expectedValue);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testInterpolationLoop() throws Exception {
-        config.setProperty("test.a", "${test.b}");
-        config.setProperty("test.b", "${test.a}");
-        config.getString("test.a");
+    public void testThrowExceptionOnMissing() {
+        assertFalse("Throw Exception Property is set!", config.isThrowExceptionOnMissing());
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestCatalogResolver.java b/src/test/java/org/apache/commons/configuration2/TestCatalogResolver.java
index 1aed682..9799394 100644
--- a/src/test/java/org/apache/commons/configuration2/TestCatalogResolver.java
+++ b/src/test/java/org/apache/commons/configuration2/TestCatalogResolver.java
@@ -41,6 +41,17 @@ public class TestCatalogResolver {
     private CatalogResolver resolver;
     private XMLConfiguration config;
 
+    /**
+     * Loads the test configuration from the specified file.
+     *
+     * @param fileName the file name
+     * @throws ConfigurationException if an error occurs
+     */
+    private void load(final String fileName) throws ConfigurationException {
+        final FileHandler handler = new FileHandler(config);
+        handler.load(fileName);
+    }
+
     @Before
     public void setUp() throws Exception {
         resolver = new CatalogResolver();
@@ -56,15 +67,19 @@ public class TestCatalogResolver {
         config = null;
     }
 
-    /**
-     * Loads the test configuration from the specified file.
-     *
-     * @param fileName the file name
-     * @throws ConfigurationException if an error occurs
-     */
-    private void load(final String fileName) throws ConfigurationException {
-        final FileHandler handler = new FileHandler(config);
-        handler.load(fileName);
+    @Test
+    public void testDebug() throws Exception {
+        resolver.setDebug(true);
+        // There is no really good way to check this except to do something
+        // that causes debug output.
+    }
+
+    @Test
+    public void testLogger() throws Exception {
+        final ConfigurationLogger log = new ConfigurationLogger(this.getClass());
+        resolver.setLogger(log);
+        assertNotNull("No Logger returned", resolver.getLogger());
+        assertSame("Incorrect Logger", log, resolver.getLogger());
     }
 
     @Test
@@ -87,19 +102,4 @@ public class TestCatalogResolver {
         load(REWRITE_SCHEMA_FILE);
     }
 
-    @Test
-    public void testDebug() throws Exception {
-        resolver.setDebug(true);
-        // There is no really good way to check this except to do something
-        // that causes debug output.
-    }
-
-    @Test
-    public void testLogger() throws Exception {
-        final ConfigurationLogger log = new ConfigurationLogger(this.getClass());
-        resolver.setLogger(log);
-        assertNotNull("No Logger returned", resolver.getLogger());
-        assertSame("Incorrect Logger", log, resolver.getLogger());
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
index 0c35390..5c64d21 100644
--- a/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java
@@ -63,6 +63,163 @@ import org.junit.rules.TemporaryFolder;
  *
  */
 public class TestCombinedConfiguration {
+    /**
+     * Test event listener class for checking if the expected invalidate events are fired.
+     */
+    private static class CombinedListener implements EventListener<ConfigurationEvent> {
+        int invalidateEvents;
+
+        int otherEvents;
+
+        /**
+         * Checks if the expected number of events was fired.
+         *
+         * @param expectedInvalidate the expected number of invalidate events
+         * @param expectedOthers the expected number of other events
+         */
+        public void checkEvent(final int expectedInvalidate, final int expectedOthers) {
+            assertEquals("Wrong number of invalidate events", expectedInvalidate, invalidateEvents);
+            assertEquals("Wrong number of other events", expectedOthers, otherEvents);
+        }
+
+        @Override
+        public void onEvent(final ConfigurationEvent event) {
+            if (event.getEventType() == CombinedConfiguration.COMBINED_INVALIDATE) {
+                invalidateEvents++;
+            } else {
+                otherEvents++;
+            }
+        }
+    }
+
+    /**
+     * A test thread performing reads on a combined configuration. This thread reads a certain property from the
+     * configuration. If everything works well, this property should have at least one and at most two values.
+     */
+    private static class ReadThread extends Thread {
+        /** The configuration to be accessed. */
+        private final Configuration config;
+
+        /** The latch for synchronizing thread start. */
+        private final CountDownLatch startLatch;
+
+        /** A counter for read errors. */
+        private final AtomicInteger errorCount;
+
+        /** The number of reads to be performed. */
+        private final int numberOfReads;
+
+        /**
+         * Creates a new instance of {@code ReadThread}.
+         *
+         * @param readConfig the configuration to be read
+         * @param latch the latch for synchronizing thread start
+         * @param errCnt the counter for read errors
+         * @param readCount the number of reads to be performed
+         */
+        public ReadThread(final Configuration readConfig, final CountDownLatch latch, final AtomicInteger errCnt, final int readCount) {
+            config = readConfig;
+            startLatch = latch;
+            errorCount = errCnt;
+            numberOfReads = readCount;
+        }
+
+        /**
+         * Reads the test property from the associated configuration. Its values are checked.
+         */
+        private void readConfiguration() {
+            final List<Object> values = config.getList(KEY_CONCURRENT);
+            if (values.size() < 1 || values.size() > 2) {
+                errorCount.incrementAndGet();
+            } else {
+                boolean ok = true;
+                for (final Object value : values) {
+                    if (!TEST_NAME.equals(value)) {
+                        ok = false;
+                    }
+                }
+                if (!ok) {
+                    errorCount.incrementAndGet();
+                }
+            }
+        }
+
+        /**
+         * Reads from the test configuration.
+         */
+        @Override
+        public void run() {
+            try {
+                startLatch.await();
+                for (int i = 0; i < numberOfReads; i++) {
+                    readConfiguration();
+                }
+            } catch (final Exception e) {
+                errorCount.incrementAndGet();
+            }
+        }
+    }
+
+    /**
+     * A test thread performing updates on a test configuration. This thread modifies configurations which are children of a
+     * combined configuration. Each update operation adds a value to one of the child configurations and removes it from
+     * another one (which contained it before). So if concurrent reads are performed, the test property should always have
+     * between 1 and 2 values.
+     */
+    private static class WriteThread extends Thread {
+        /** The list with the child configurations. */
+        private final List<Configuration> testConfigs;
+
+        /** The latch for synchronizing thread start. */
+        private final CountDownLatch startLatch;
+
+        /** A counter for errors. */
+        private final AtomicInteger errorCount;
+
+        /** The number of write operations to be performed. */
+        private final int numberOfWrites;
+
+        /** The index of the child configuration containing the test property. */
+        private int currentChildConfigIdx;
+
+        /**
+         * Creates a new instance of {@code WriteThread}.
+         *
+         * @param cc the test combined configuration
+         * @param latch the latch for synchronizing test start
+         * @param errCnt a counter for errors
+         * @param writeCount the number of writes to be performed
+         */
+        public WriteThread(final CombinedConfiguration cc, final CountDownLatch latch, final AtomicInteger errCnt, final int writeCount) {
+            testConfigs = cc.getConfigurations();
+            startLatch = latch;
+            errorCount = errCnt;
+            numberOfWrites = writeCount;
+        }
+
+        @Override
+        public void run() {
+            try {
+                startLatch.await();
+                for (int i = 0; i < numberOfWrites; i++) {
+                    updateConfigurations();
+                }
+            } catch (final InterruptedException e) {
+                errorCount.incrementAndGet();
+            }
+        }
+
+        /**
+         * Performs the update operation.
+         */
+        private void updateConfigurations() {
+            final int newIdx = (currentChildConfigIdx + 1) % testConfigs.size();
+            testConfigs.get(newIdx).addProperty(KEY_CONCURRENT, TEST_NAME);
+            testConfigs.get(currentChildConfigIdx).clearProperty(KEY_CONCURRENT);
+            currentChildConfigIdx = newIdx;
+        }
+    }
+
     /** Constant for the name of a sub configuration. */
     private static final String TEST_NAME = "SUBCONFIG";
 
@@ -81,6 +238,18 @@ public class TestCombinedConfiguration {
     /** Constant for the key for a sub configuration. */
     private static final String SUB_KEY = "test.sub.config";
 
+    /**
+     * Helper method for creating a test configuration to be added to the combined configuration.
+     *
+     * @return the test configuration
+     */
+    private static AbstractConfiguration setUpTestConfiguration() {
+        final BaseHierarchicalConfiguration config = new BaseHierarchicalConfiguration();
+        config.addProperty(TEST_KEY, Boolean.TRUE);
+        config.addProperty("test.comment", "This is a test");
+        return config;
+    }
+
     /** Helper object for managing temporary files. */
     @Rule
     public TemporaryFolder folder = new TemporaryFolder();
@@ -91,105 +260,90 @@ public class TestCombinedConfiguration {
     /** The test event listener. */
     private CombinedListener listener;
 
-    @Before
-    public void setUp() throws Exception {
-        config = new CombinedConfiguration();
-        listener = new CombinedListener();
-        config.addEventListener(ConfigurationEvent.ANY, listener);
-    }
-
     /**
-     * Tests accessing a newly created combined configuration.
+     * Checks if a configuration was correctly added to the combined config.
+     *
+     * @param c the config to check
      */
-    @Test
-    public void testInit() {
-        assertEquals("Already configurations contained", 0, config.getNumberOfConfigurations());
-        assertTrue("Set of names is not empty", config.getConfigurationNames().isEmpty());
-        assertTrue("Wrong node combiner", config.getNodeCombiner() instanceof UnionCombiner);
-        assertNull("Test config was found", config.getConfiguration(TEST_NAME));
+    private void checkAddConfig(final AbstractConfiguration c) {
+        final Collection<EventListener<? super ConfigurationEvent>> listeners = c.getEventListeners(ConfigurationEvent.ANY);
+        assertEquals("Wrong number of configuration listeners", 1, listeners.size());
+        assertTrue("Combined config is no listener", listeners.contains(config));
     }
 
     /**
-     * Tests adding a configuration (without further information).
+     * Helper method for testing that the combined root node has not yet been constructed.
      */
-    @Test
-    public void testAddConfiguration() {
-        final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c);
-        checkAddConfig(c);
-        assertEquals("Wrong number of configs", 1, config.getNumberOfConfigurations());
-        assertTrue("Name list is not empty", config.getConfigurationNames().isEmpty());
-        assertSame("Added config not found", c, config.getConfiguration(0));
-        assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
-        listener.checkEvent(1, 0);
+    private void checkCombinedRootNotConstructed() {
+        assertTrue("Root node was constructed", config.getModel().getNodeHandler().getRootNode().getChildren().isEmpty());
     }
 
     /**
-     * Tests adding a configuration with a name.
+     * Checks the configurationsAt() method.
+     *
+     * @param withUpdates flag whether updates are supported
      */
-    @Test
-    public void testAddConfigurationWithName() {
-        final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c, TEST_NAME);
-        checkAddConfig(c);
-        assertEquals("Wrong number of configs", 1, config.getNumberOfConfigurations());
-        assertSame("Added config not found", c, config.getConfiguration(0));
-        assertSame("Added config not found by name", c, config.getConfiguration(TEST_NAME));
-        final Set<String> names = config.getConfigurationNames();
-        assertEquals("Wrong number of config names", 1, names.size());
-        assertTrue("Name not found", names.contains(TEST_NAME));
-        assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
-        listener.checkEvent(1, 0);
+    private void checkConfigurationsAt(final boolean withUpdates) {
+        setUpSubConfigTest();
+        final List<HierarchicalConfiguration<ImmutableNode>> subs = config.configurationsAt(SUB_KEY, withUpdates);
+        assertEquals("Wrong number of sub configurations", 1, subs.size());
+        assertTrue("Wrong value in sub configuration", subs.get(0).getBoolean(TEST_KEY));
     }
 
     /**
-     * Tests adding a configuration with a name when this name already exists. This should cause an exception.
+     * Tests whether a configuration was completely removed.
+     *
+     * @param c the removed configuration
      */
-    @Test(expected = ConfigurationRuntimeException.class)
-    public void testAddConfigurationWithNameTwice() {
-        config.addConfiguration(setUpTestConfiguration(), TEST_NAME);
-        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "prefix");
+    private void checkRemoveConfig(final AbstractConfiguration c) {
+        assertTrue("Listener was not removed", c.getEventListeners(ConfigurationEvent.ANY).isEmpty());
+        assertEquals("Wrong number of contained configs", 0, config.getNumberOfConfigurations());
+        assertTrue("Name was not removed", config.getConfigurationNames().isEmpty());
+        listener.checkEvent(2, 0);
     }
 
-    /**
-     * Tests adding a configuration and specifying an at position.
-     */
-    @Test
-    public void testAddConfigurationAt() {
-        final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c, null, "my");
-        checkAddConfig(c);
-        assertTrue("Wrong property value", config.getBoolean("my." + TEST_KEY));
+    @Before
+    public void setUp() throws Exception {
+        config = new CombinedConfiguration();
+        listener = new CombinedListener();
+        config.addEventListener(ConfigurationEvent.ANY, listener);
     }
 
     /**
-     * Tests adding a configuration with a complex at position. Here the at path contains a dot, which must be escaped.
+     * Prepares a test of the getSource() method.
      */
-    @Test
-    public void testAddConfigurationComplexAt() {
-        final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c, null, "This..is.a.complex");
-        checkAddConfig(c);
-        assertTrue("Wrong property value", config.getBoolean("This..is.a.complex." + TEST_KEY));
+    private void setUpSourceTest() {
+        final BaseHierarchicalConfiguration c1 = new BaseHierarchicalConfiguration();
+        final PropertiesConfiguration c2 = new PropertiesConfiguration();
+        c1.addProperty(TEST_KEY, TEST_NAME);
+        c2.addProperty("another.key", "test");
+        config.addConfiguration(c1, CHILD1);
+        config.addConfiguration(c2, CHILD2);
     }
 
     /**
-     * Checks if a configuration was correctly added to the combined config.
+     * Prepares the test configuration for a test for sub configurations. Some child configurations are added.
      *
-     * @param c the config to check
+     * @return the sub configuration at the test sub key
      */
-    private void checkAddConfig(final AbstractConfiguration c) {
-        final Collection<EventListener<? super ConfigurationEvent>> listeners = c.getEventListeners(ConfigurationEvent.ANY);
-        assertEquals("Wrong number of configuration listeners", 1, listeners.size());
-        assertTrue("Combined config is no listener", listeners.contains(config));
+    private AbstractConfiguration setUpSubConfigTest() {
+        final AbstractConfiguration srcConfig = setUpTestConfiguration();
+        config.addConfiguration(srcConfig, "source", SUB_KEY);
+        config.addConfiguration(setUpTestConfiguration());
+        config.addConfiguration(setUpTestConfiguration(), "otherTest", "other.prefix");
+        return srcConfig;
     }
 
     /**
-     * Tests adding a null configuration. This should cause an exception to be thrown.
+     * Prepares a test for synchronization. This method installs a test synchronizer and adds some test configurations.
+     *
+     * @return the test synchronizer
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void testAddNullConfiguration() {
-        config.addConfiguration(null);
+    private SynchronizerTestImpl setUpSynchronizerTest() {
+        setUpSourceTest();
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        config.setSynchronizer(sync);
+        return sync;
     }
 
     /**
@@ -218,120 +372,117 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests removing a configuration.
+     * Tests adding a configuration (without further information).
      */
     @Test
-    public void testRemoveConfiguration() {
+    public void testAddConfiguration() {
         final AbstractConfiguration c = setUpTestConfiguration();
         config.addConfiguration(c);
         checkAddConfig(c);
-        assertTrue("Config could not be removed", config.removeConfiguration(c));
-        checkRemoveConfig(c);
+        assertEquals("Wrong number of configs", 1, config.getNumberOfConfigurations());
+        assertTrue("Name list is not empty", config.getConfigurationNames().isEmpty());
+        assertSame("Added config not found", c, config.getConfiguration(0));
+        assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
+        listener.checkEvent(1, 0);
     }
 
     /**
-     * Tests removing a configuration by index.
+     * Tests adding a configuration and specifying an at position.
      */
     @Test
-    public void testRemoveConfigurationAt() {
+    public void testAddConfigurationAt() {
         final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c);
-        assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
-        checkRemoveConfig(c);
+        config.addConfiguration(c, null, "my");
+        checkAddConfig(c);
+        assertTrue("Wrong property value", config.getBoolean("my." + TEST_KEY));
     }
 
     /**
-     * Tests removing a configuration by name.
+     * Tests adding a configuration with a complex at position. Here the at path contains a dot, which must be escaped.
      */
     @Test
-    public void testRemoveConfigurationByName() {
+    public void testAddConfigurationComplexAt() {
         final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c, TEST_NAME);
-        assertSame("Wrong config removed", c, config.removeConfiguration(TEST_NAME));
-        checkRemoveConfig(c);
+        config.addConfiguration(c, null, "This..is.a.complex");
+        checkAddConfig(c);
+        assertTrue("Wrong property value", config.getBoolean("This..is.a.complex." + TEST_KEY));
     }
 
     /**
-     * Tests removing a configuration with a name.
+     * Tests whether adding a new configuration is synchronized.
      */
     @Test
-    public void testRemoveNamedConfiguration() {
-        final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c, TEST_NAME);
-        config.removeConfiguration(c);
-        checkRemoveConfig(c);
+    public void testAddConfigurationSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        config.addConfiguration(new BaseHierarchicalConfiguration());
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+        checkCombinedRootNotConstructed();
     }
 
     /**
-     * Tests removing a named configuration by index.
+     * Tests adding a configuration with a name.
      */
     @Test
-    public void testRemoveNamedConfigurationAt() {
+    public void testAddConfigurationWithName() {
         final AbstractConfiguration c = setUpTestConfiguration();
         config.addConfiguration(c, TEST_NAME);
-        assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
-        checkRemoveConfig(c);
-    }
-
-    /**
-     * Tests removing a configuration that was not added prior.
-     */
-    @Test
-    public void testRemoveNonContainedConfiguration() {
-        assertFalse("Could remove non contained config", config.removeConfiguration(setUpTestConfiguration()));
-        listener.checkEvent(0, 0);
+        checkAddConfig(c);
+        assertEquals("Wrong number of configs", 1, config.getNumberOfConfigurations());
+        assertSame("Added config not found", c, config.getConfiguration(0));
+        assertSame("Added config not found by name", c, config.getConfiguration(TEST_NAME));
+        final Set<String> names = config.getConfigurationNames();
+        assertEquals("Wrong number of config names", 1, names.size());
+        assertTrue("Name not found", names.contains(TEST_NAME));
+        assertTrue("Wrong property value", config.getBoolean(TEST_KEY));
+        listener.checkEvent(1, 0);
     }
 
     /**
-     * Tests removing a configuration by name, which is not contained.
+     * Tests adding a configuration with a name when this name already exists. This should cause an exception.
      */
-    @Test
-    public void testRemoveConfigurationByUnknownName() {
-        assertNull("Could remove configuration by unknown name", config.removeConfiguration("unknownName"));
-        listener.checkEvent(0, 0);
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testAddConfigurationWithNameTwice() {
+        config.addConfiguration(setUpTestConfiguration(), TEST_NAME);
+        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "prefix");
     }
 
     /**
-     * Tests whether a configuration was completely removed.
-     *
-     * @param c the removed configuration
+     * Tests adding a null configuration. This should cause an exception to be thrown.
      */
-    private void checkRemoveConfig(final AbstractConfiguration c) {
-        assertTrue("Listener was not removed", c.getEventListeners(ConfigurationEvent.ANY).isEmpty());
-        assertEquals("Wrong number of contained configs", 0, config.getNumberOfConfigurations());
-        assertTrue("Name was not removed", config.getConfigurationNames().isEmpty());
-        listener.checkEvent(2, 0);
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddNullConfiguration() {
+        config.addConfiguration(null);
     }
 
     /**
-     * Tests if an update of a contained configuration leeds to an invalidation of the combined configuration.
+     * Tests clearing a combined configuration. This should remove all contained configurations.
      */
     @Test
-    public void testUpdateContainedConfiguration() {
-        final AbstractConfiguration c = setUpTestConfiguration();
-        config.addConfiguration(c);
-        c.addProperty("test.otherTest", "yes");
-        assertEquals("New property not found", "yes", config.getString("test.otherTest"));
-        listener.checkEvent(2, 0);
+    public void testClear() {
+        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "test");
+        config.addConfiguration(setUpTestConfiguration());
+
+        config.clear();
+        assertEquals("Still configs contained", 0, config.getNumberOfConfigurations());
+        assertTrue("Still names contained", config.getConfigurationNames().isEmpty());
+        assertTrue("Config is not empty", config.isEmpty());
+
+        listener.checkEvent(3, 2);
     }
 
     /**
-     * Tests if setting a node combiner causes an invalidation.
+     * Tests whether the combined configuration removes itself as change listener from the child configurations on a clear
+     * operation. This test is related to CONFIGURATION-572.
      */
     @Test
-    public void testSetNodeCombiner() {
-        final NodeCombiner combiner = new UnionCombiner();
-        config.setNodeCombiner(combiner);
-        assertSame("Node combiner was not set", combiner, config.getNodeCombiner());
-        listener.checkEvent(1, 0);
-    }
+    public void testClearRemoveChildListener() {
+        final AbstractConfiguration child = setUpTestConfiguration();
+        config.addConfiguration(child);
 
-    /**
-     * Tests setting a null node combiner. This should cause an exception.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testSetNullNodeCombiner() {
-        config.setNodeCombiner(null);
+        config.clear();
+        for (final EventListener<?> listener : child.getEventListeners(ConfigurationEvent.ANY)) {
+            assertNotEquals("Still registered", config, listener);
+        }
     }
 
     /**
@@ -374,152 +525,134 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests clearing a combined configuration. This should remove all contained configurations.
+     * Tests whether cloning of a configuration is correctly synchronized.
      */
     @Test
-    public void testClear() {
-        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "test");
-        config.addConfiguration(setUpTestConfiguration());
-
-        config.clear();
-        assertEquals("Still configs contained", 0, config.getNumberOfConfigurations());
-        assertTrue("Still names contained", config.getConfigurationNames().isEmpty());
-        assertTrue("Config is not empty", config.isEmpty());
-
-        listener.checkEvent(3, 2);
+    public void testCloneSynchronized() {
+        setUpSourceTest();
+        config.lock(LockMode.READ); // Causes the root node to be constructed
+        config.unlock(LockMode.READ);
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        config.setSynchronizer(sync);
+        config.clone();
+        // clone() of base class is wrapped by another read lock
+        sync.verifyStart(Methods.BEGIN_READ, Methods.BEGIN_READ);
+        sync.verifyEnd(Methods.END_READ, Methods.END_READ);
     }
 
     /**
-     * Tests whether the combined configuration removes itself as change listener from the child configurations on a clear
-     * operation. This test is related to CONFIGURATION-572.
+     * Tests whether a combined configuration can be copied to an XML configuration. This test is related to
+     * CONFIGURATION-445.
      */
     @Test
-    public void testClearRemoveChildListener() {
-        final AbstractConfiguration child = setUpTestConfiguration();
-        config.addConfiguration(child);
-
-        config.clear();
-        for (final EventListener<?> listener : child.getEventListeners(ConfigurationEvent.ANY)) {
-            assertNotEquals("Still registered", config, listener);
-        }
-    }
-
-    /**
-     * Prepares a test of the getSource() method.
-     */
-    private void setUpSourceTest() {
-        final BaseHierarchicalConfiguration c1 = new BaseHierarchicalConfiguration();
-        final PropertiesConfiguration c2 = new PropertiesConfiguration();
-        c1.addProperty(TEST_KEY, TEST_NAME);
-        c2.addProperty("another.key", "test");
-        config.addConfiguration(c1, CHILD1);
-        config.addConfiguration(c2, CHILD2);
+    public void testCombinedCopyToXML() throws ConfigurationException {
+        final XMLConfiguration x1 = new XMLConfiguration();
+        x1.addProperty("key1", "value1");
+        x1.addProperty("key1[@override]", "USER1");
+        x1.addProperty("key2", "value2");
+        x1.addProperty("key2[@override]", "USER2");
+        final XMLConfiguration x2 = new XMLConfiguration();
+        x2.addProperty("key2", "value2.2");
+        x2.addProperty("key2[@override]", "USER2");
+        config.setNodeCombiner(new OverrideCombiner());
+        config.addConfiguration(x2);
+        config.addConfiguration(x1);
+        XMLConfiguration x3 = new XMLConfiguration(config);
+        assertEquals("Wrong element value", "value2.2", x3.getString("key2"));
+        assertEquals("Wrong attribute value", "USER2", x3.getString("key2[@override]"));
+        final StringWriter w = new StringWriter();
+        new FileHandler(x3).save(w);
+        final String s = w.toString();
+        x3 = new XMLConfiguration();
+        new FileHandler(x3).load(new StringReader(s));
+        assertEquals("Wrong element value after load", "value2.2", x3.getString("key2"));
+        assertEquals("Wrong attribute value after load", "USER2", x3.getString("key2[@override]"));
     }
 
     /**
-     * Tests the gestSource() method when the source property is defined in a hierarchical configuration.
+     * Tests concurrent read and write access on a combined configuration. There are multiple reader threads and a single
+     * writer thread. It is checked that no inconsistencies occur.
      */
     @Test
-    public void testGetSourceHierarchical() {
+    public void testConcurrentAccess() throws ConfigurationException, InterruptedException {
+        // populate the test combined configuration
         setUpSourceTest();
-        assertEquals("Wrong source configuration", config.getConfiguration(CHILD1), config.getSource(TEST_KEY));
-    }
+        final XMLConfiguration xmlConf = new XMLConfiguration();
+        new FileHandler(xmlConf).load(ConfigurationAssert.getTestFile("test.xml"));
+        config.addConfiguration(xmlConf);
+        final PropertiesConfiguration propConf = new PropertiesConfiguration();
+        new FileHandler(propConf).load(ConfigurationAssert.getTestFile("test.properties"));
+        for (int i = 0; i < 8; i++) {
+            config.addConfiguration(new BaseHierarchicalConfiguration());
+        }
+        config.getConfiguration(0).addProperty(KEY_CONCURRENT, TEST_NAME);
 
-    /**
-     * Tests whether the source configuration can be detected for non hierarchical configurations.
-     */
-    @Test
-    public void testGetSourceNonHierarchical() {
-        setUpSourceTest();
-        assertEquals("Wrong source configuration", config.getConfiguration(CHILD2), config.getSource("another.key"));
-    }
-
-    /**
-     * Tests the getSource() method when the passed in key is not contained. Result should be null in this case.
-     */
-    @Test
-    public void testGetSourceUnknown() {
-        setUpSourceTest();
-        assertNull("Wrong result for unknown key", config.getSource("an.unknown.key"));
-    }
-
-    /**
-     * Tests the getSource() method when a null key is passed in. This should cause an exception.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetSourceNull() {
-        config.getSource(null);
-    }
-
-    /**
-     * Tests the getSource() method when the passed in key belongs to the combined configuration itself.
-     */
-    @Test
-    public void testGetSourceCombined() {
-        setUpSourceTest();
-        final String key = "yet.another.key";
-        config.addProperty(key, Boolean.TRUE);
-        assertEquals("Wrong source for key", config, config.getSource(key));
-    }
+        // Set a single synchronizer for all involved configurations
+        final Synchronizer sync = new ReadWriteSynchronizer();
+        config.setSynchronizer(sync);
+        for (final Configuration c : config.getConfigurations()) {
+            c.setSynchronizer(sync);
+        }
 
-    /**
-     * Tests the getSource() method when the passed in key refers to multiple values, which are all defined in the same
-     * source configuration.
-     */
-    @Test
-    public void testGetSourceMulti() {
-        setUpSourceTest();
-        final String key = "list.key";
-        config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
-        assertEquals("Wrong source for multi-value property", config.getConfiguration(CHILD1), config.getSource(key));
-    }
+        // setup test threads
+        final int numberOfReaders = 3;
+        final int readCount = 5000;
+        final int writeCount = 3000;
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AtomicInteger errorCount = new AtomicInteger();
+        final Collection<Thread> threads = new ArrayList<>(numberOfReaders + 1);
+        final Thread writeThread = new WriteThread(config, latch, errorCount, writeCount);
+        writeThread.start();
+        threads.add(writeThread);
+        for (int i = 0; i < numberOfReaders; i++) {
+            final Thread readThread = new ReadThread(config, latch, errorCount, readCount);
+            readThread.start();
+            threads.add(readThread);
+        }
 
-    /**
-     * Tests the getSource() method when the passed in key refers to multiple values defined by different sources. This
-     * should cause an exception.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetSourceMultiSources() {
-        setUpSourceTest();
-        final String key = "list.key";
-        config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
-        config.getConfiguration(CHILD2).addProperty(key, "a,b,c");
-        config.getSource(key);
+        // perform test
+        latch.countDown();
+        for (final Thread t : threads) {
+            t.join();
+        }
+        assertEquals("Got errors", 0, errorCount.get());
     }
 
     /**
-     * Tests getSource() if a child configuration is again a combined configuration.
+     * Tests whether sub configurations can be created from a key.
      */
     @Test
-    public void testGetSourceWithCombinedChildConfiguration() {
-        setUpSourceTest();
-        final CombinedConfiguration cc = new CombinedConfiguration();
-        cc.addConfiguration(config);
-        assertEquals("Wrong source", config, cc.getSource(TEST_KEY));
+    public void testConfigurationsAt() {
+        checkConfigurationsAt(false);
     }
 
     /**
-     * Tests whether multiple sources of a key can be retrieved.
+     * Tests whether sub configurations can be created which are attached.
      */
     @Test
-    public void testGetSourcesMultiSources() {
-        setUpSourceTest();
-        final String key = "list.key";
-        config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
-        config.getConfiguration(CHILD2).addProperty(key, "a,b,c");
-        final Set<Configuration> sources = config.getSources(key);
-        assertEquals("Wrong number of sources", 2, sources.size());
-        assertTrue("Source 1 not found", sources.contains(config.getConfiguration(CHILD1)));
-        assertTrue("Source 2 not found", sources.contains(config.getConfiguration(CHILD2)));
+    public void testConfigurationsAtWithUpdates() {
+        checkConfigurationsAt(true);
     }
 
     /**
-     * Tests getSources() for a non existing key.
+     * Tests using a conversion expression engine for child configurations with strange keys. This test is related to
+     * CONFIGURATION-336.
      */
     @Test
-    public void testGetSourcesUnknownKey() {
-        setUpSourceTest();
-        assertTrue("Got sources", config.getSources("non.existing,key").isEmpty());
+    public void testConversionExpressionEngine() {
+        final PropertiesConfiguration child = new PropertiesConfiguration();
+        child.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        child.addProperty("test(a)", "1,2,3");
+        config.addConfiguration(child);
+        final DefaultExpressionEngine engineQuery = new DefaultExpressionEngine(
+            new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setIndexStart("<").setIndexEnd(">").create());
+        config.setExpressionEngine(engineQuery);
+        final DefaultExpressionEngine engineConvert = new DefaultExpressionEngine(
+            new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setIndexStart("[").setIndexEnd("]").create());
+        config.setConversionExpressionEngine(engineConvert);
+        assertEquals("Wrong property 1", "1", config.getString("test(a)<0>"));
+        assertEquals("Wrong property 2", "2", config.getString("test(a)<1>"));
+        assertEquals("Wrong property 3", "3", config.getString("test(a)<2>"));
     }
 
     /**
@@ -535,50 +668,25 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests whether only a single invalidate event is fired for a change. This test is related to CONFIGURATION-315.
+     * Tests whether access to a configuration by index is correctly synchronized.
      */
     @Test
-    public void testInvalidateEventBeforeAndAfterChange() {
-        ConfigurationEvent event = new ConfigurationEvent(config, ConfigurationEvent.ANY, null, null, true);
-        config.onEvent(event);
-        assertEquals("No invalidate event fired", 1, listener.invalidateEvents);
-        event = new ConfigurationEvent(config, ConfigurationEvent.ANY, null, null, false);
-        config.onEvent(event);
-        assertEquals("Another invalidate event fired", 1, listener.invalidateEvents);
+    public void testGetConfigurationByIdxSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        assertNotNull("No configuration", config.getConfiguration(0));
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+        checkCombinedRootNotConstructed();
     }
 
     /**
-     * Tests using a conversion expression engine for child configurations with strange keys. This test is related to
-     * CONFIGURATION-336.
+     * Tests whether access to a configuration by name is correctly synchronized.
      */
     @Test
-    public void testConversionExpressionEngine() {
-        final PropertiesConfiguration child = new PropertiesConfiguration();
-        child.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        child.addProperty("test(a)", "1,2,3");
-        config.addConfiguration(child);
-        final DefaultExpressionEngine engineQuery = new DefaultExpressionEngine(
-            new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setIndexStart("<").setIndexEnd(">").create());
-        config.setExpressionEngine(engineQuery);
-        final DefaultExpressionEngine engineConvert = new DefaultExpressionEngine(
-            new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setIndexStart("[").setIndexEnd("]").create());
-        config.setConversionExpressionEngine(engineConvert);
-        assertEquals("Wrong property 1", "1", config.getString("test(a)<0>"));
-        assertEquals("Wrong property 2", "2", config.getString("test(a)<1>"));
-        assertEquals("Wrong property 3", "3", config.getString("test(a)<2>"));
-    }
-
-    @Test
-    public void testGetConfigurations() throws Exception {
-        config.addConfiguration(setUpTestConfiguration());
-        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
-        final AbstractConfiguration pc = new PropertiesConfiguration();
-        config.addConfiguration(pc, "props");
-        final List<Configuration> list = config.getConfigurations();
-        assertNotNull("No list of configurations returned", list);
-        assertTrue("Incorrect number of configurations", list.size() == 3);
-        final Configuration c = list.get(2);
-        assertTrue("Incorrect configuration", c == pc);
+    public void testGetConfigurationByNameSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        assertNotNull("No configuration", config.getConfiguration(CHILD1));
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+        checkCombinedRootNotConstructed();
     }
 
     @Test
@@ -596,65 +704,59 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests whether a combined configuration can be copied to an XML configuration. This test is related to
-     * CONFIGURATION-445.
+     * Tests whether querying the name list of child configurations is synchronized.
      */
     @Test
-    public void testCombinedCopyToXML() throws ConfigurationException {
-        final XMLConfiguration x1 = new XMLConfiguration();
-        x1.addProperty("key1", "value1");
-        x1.addProperty("key1[@override]", "USER1");
-        x1.addProperty("key2", "value2");
-        x1.addProperty("key2[@override]", "USER2");
-        final XMLConfiguration x2 = new XMLConfiguration();
-        x2.addProperty("key2", "value2.2");
-        x2.addProperty("key2[@override]", "USER2");
-        config.setNodeCombiner(new OverrideCombiner());
-        config.addConfiguration(x2);
-        config.addConfiguration(x1);
-        XMLConfiguration x3 = new XMLConfiguration(config);
-        assertEquals("Wrong element value", "value2.2", x3.getString("key2"));
-        assertEquals("Wrong attribute value", "USER2", x3.getString("key2[@override]"));
-        final StringWriter w = new StringWriter();
-        new FileHandler(x3).save(w);
-        final String s = w.toString();
-        x3 = new XMLConfiguration();
-        new FileHandler(x3).load(new StringReader(s));
-        assertEquals("Wrong element value after load", "value2.2", x3.getString("key2"));
-        assertEquals("Wrong attribute value after load", "USER2", x3.getString("key2[@override]"));
+    public void testGetConfigurationNameListSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        assertFalse("No child names", config.getConfigurationNameList().isEmpty());
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+        checkCombinedRootNotConstructed();
     }
 
     /**
-     * Prepares a test for synchronization. This method installs a test synchronizer and adds some test configurations.
-     *
-     * @return the test synchronizer
+     * Tests whether querying the name set of child configurations is synchronized.
      */
-    private SynchronizerTestImpl setUpSynchronizerTest() {
-        setUpSourceTest();
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        config.setSynchronizer(sync);
-        return sync;
+    @Test
+    public void testGetConfigurationNamesSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        assertFalse("No child names", config.getConfigurationNames().isEmpty());
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+        checkCombinedRootNotConstructed();
+    }
+
+    @Test
+    public void testGetConfigurations() throws Exception {
+        config.addConfiguration(setUpTestConfiguration());
+        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
+        final AbstractConfiguration pc = new PropertiesConfiguration();
+        config.addConfiguration(pc, "props");
+        final List<Configuration> list = config.getConfigurations();
+        assertNotNull("No list of configurations returned", list);
+        assertTrue("Incorrect number of configurations", list.size() == 3);
+        final Configuration c = list.get(2);
+        assertTrue("Incorrect configuration", c == pc);
     }
 
     /**
-     * Tests whether adding a new configuration is synchronized.
+     * Tests whether querying the list of child configurations is synchronized.
      */
     @Test
-    public void testAddConfigurationSynchronized() {
+    public void testGetConfigurationsSynchronized() {
         final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        config.addConfiguration(new BaseHierarchicalConfiguration());
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+        assertFalse("No child configurations", config.getConfigurations().isEmpty());
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
         checkCombinedRootNotConstructed();
     }
 
     /**
-     * Tests whether setNodeCombiner() is correctly synchronized.
+     * Tests whether read access to the conversion expression engine is synchronized.
      */
     @Test
-    public void testSetNodeCombinerSynchronized() {
+    public void testGetConversionExpressionEngineSynchronized() {
         final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        config.setNodeCombiner(new UnionCombiner());
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+        assertNull("Got a conversion engine", config.getConversionExpressionEngine());
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
         checkCombinedRootNotConstructed();
     }
 
@@ -670,97 +772,100 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests whether access to a configuration by index is correctly synchronized.
+     * Tests whether querying the number of child configurations is synchronized.
      */
     @Test
-    public void testGetConfigurationByIdxSynchronized() {
+    public void testGetNumberOfConfigurationsSynchronized() {
         final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertNotNull("No configuration", config.getConfiguration(0));
+        assertEquals("Wrong number of configurations", 2, config.getNumberOfConfigurations());
         sync.verify(Methods.BEGIN_READ, Methods.END_READ);
         checkCombinedRootNotConstructed();
     }
 
     /**
-     * Tests whether access to a configuration by name is correctly synchronized.
+     * Tests the getSource() method when the passed in key belongs to the combined configuration itself.
      */
     @Test
-    public void testGetConfigurationByNameSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertNotNull("No configuration", config.getConfiguration(CHILD1));
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        checkCombinedRootNotConstructed();
+    public void testGetSourceCombined() {
+        setUpSourceTest();
+        final String key = "yet.another.key";
+        config.addProperty(key, Boolean.TRUE);
+        assertEquals("Wrong source for key", config, config.getSource(key));
     }
 
     /**
-     * Tests whether querying the name set of child configurations is synchronized.
+     * Tests the gestSource() method when the source property is defined in a hierarchical configuration.
      */
     @Test
-    public void testGetConfigurationNamesSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertFalse("No child names", config.getConfigurationNames().isEmpty());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        checkCombinedRootNotConstructed();
+    public void testGetSourceHierarchical() {
+        setUpSourceTest();
+        assertEquals("Wrong source configuration", config.getConfiguration(CHILD1), config.getSource(TEST_KEY));
     }
 
     /**
-     * Tests whether querying the name list of child configurations is synchronized.
+     * Tests the getSource() method when the passed in key refers to multiple values, which are all defined in the same
+     * source configuration.
      */
     @Test
-    public void testGetConfigurationNameListSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertFalse("No child names", config.getConfigurationNameList().isEmpty());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        checkCombinedRootNotConstructed();
+    public void testGetSourceMulti() {
+        setUpSourceTest();
+        final String key = "list.key";
+        config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
+        assertEquals("Wrong source for multi-value property", config.getConfiguration(CHILD1), config.getSource(key));
     }
 
     /**
-     * Helper method for testing that the combined root node has not yet been constructed.
+     * Tests the getSource() method when the passed in key refers to multiple values defined by different sources. This
+     * should cause an exception.
      */
-    private void checkCombinedRootNotConstructed() {
-        assertTrue("Root node was constructed", config.getModel().getNodeHandler().getRootNode().getChildren().isEmpty());
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetSourceMultiSources() {
+        setUpSourceTest();
+        final String key = "list.key";
+        config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
+        config.getConfiguration(CHILD2).addProperty(key, "a,b,c");
+        config.getSource(key);
     }
 
     /**
-     * Tests whether querying the list of child configurations is synchronized.
+     * Tests whether the source configuration can be detected for non hierarchical configurations.
      */
     @Test
-    public void testGetConfigurationsSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertFalse("No child configurations", config.getConfigurations().isEmpty());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        checkCombinedRootNotConstructed();
+    public void testGetSourceNonHierarchical() {
+        setUpSourceTest();
+        assertEquals("Wrong source configuration", config.getConfiguration(CHILD2), config.getSource("another.key"));
     }
 
     /**
-     * Tests whether read access to the conversion expression engine is synchronized.
+     * Tests the getSource() method when a null key is passed in. This should cause an exception.
      */
-    @Test
-    public void testGetConversionExpressionEngineSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertNull("Got a conversion engine", config.getConversionExpressionEngine());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        checkCombinedRootNotConstructed();
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetSourceNull() {
+        config.getSource(null);
     }
 
     /**
-     * Tests whether write access to the conversion expression engine is synchronized.
+     * Tests whether multiple sources of a key can be retrieved.
      */
     @Test
-    public void testSetConversionExpressionEngineSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        config.setConversionExpressionEngine(new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS));
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
-        checkCombinedRootNotConstructed();
+    public void testGetSourcesMultiSources() {
+        setUpSourceTest();
+        final String key = "list.key";
+        config.getConfiguration(CHILD1).addProperty(key, "1,2,3");
+        config.getConfiguration(CHILD2).addProperty(key, "a,b,c");
+        final Set<Configuration> sources = config.getSources(key);
+        assertEquals("Wrong number of sources", 2, sources.size());
+        assertTrue("Source 1 not found", sources.contains(config.getConfiguration(CHILD1)));
+        assertTrue("Source 2 not found", sources.contains(config.getConfiguration(CHILD2)));
     }
 
     /**
-     * Tests whether invalidate() performs correct synchronization.
+     * Tests getSources() for a non existing key.
      */
     @Test
-    public void testInvalidateSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        config.invalidate();
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+    public void testGetSourcesUnknownKey() {
+        setUpSourceTest();
+        assertTrue("Got sources", config.getSources("non.existing,key").isEmpty());
     }
 
     /**
@@ -775,30 +880,57 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests whether querying the number of child configurations is synchronized.
+     * Tests the getSource() method when the passed in key is not contained. Result should be null in this case.
      */
     @Test
-    public void testGetNumberOfConfigurationsSynchronized() {
-        final SynchronizerTestImpl sync = setUpSynchronizerTest();
-        assertEquals("Wrong number of configurations", 2, config.getNumberOfConfigurations());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-        checkCombinedRootNotConstructed();
+    public void testGetSourceUnknown() {
+        setUpSourceTest();
+        assertNull("Wrong result for unknown key", config.getSource("an.unknown.key"));
     }
 
     /**
-     * Tests whether cloning of a configuration is correctly synchronized.
+     * Tests getSource() if a child configuration is again a combined configuration.
      */
     @Test
-    public void testCloneSynchronized() {
+    public void testGetSourceWithCombinedChildConfiguration() {
         setUpSourceTest();
-        config.lock(LockMode.READ); // Causes the root node to be constructed
-        config.unlock(LockMode.READ);
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        config.setSynchronizer(sync);
-        config.clone();
-        // clone() of base class is wrapped by another read lock
-        sync.verifyStart(Methods.BEGIN_READ, Methods.BEGIN_READ);
-        sync.verifyEnd(Methods.END_READ, Methods.END_READ);
+        final CombinedConfiguration cc = new CombinedConfiguration();
+        cc.addConfiguration(config);
+        assertEquals("Wrong source", config, cc.getSource(TEST_KEY));
+    }
+
+    /**
+     * Tests accessing a newly created combined configuration.
+     */
+    @Test
+    public void testInit() {
+        assertEquals("Already configurations contained", 0, config.getNumberOfConfigurations());
+        assertTrue("Set of names is not empty", config.getConfigurationNames().isEmpty());
+        assertTrue("Wrong node combiner", config.getNodeCombiner() instanceof UnionCombiner);
+        assertNull("Test config was found", config.getConfiguration(TEST_NAME));
+    }
+
+    /**
+     * Tests whether only a single invalidate event is fired for a change. This test is related to CONFIGURATION-315.
+     */
+    @Test
+    public void testInvalidateEventBeforeAndAfterChange() {
+        ConfigurationEvent event = new ConfigurationEvent(config, ConfigurationEvent.ANY, null, null, true);
+        config.onEvent(event);
+        assertEquals("No invalidate event fired", 1, listener.invalidateEvents);
+        event = new ConfigurationEvent(config, ConfigurationEvent.ANY, null, null, false);
+        config.onEvent(event);
+        assertEquals("Another invalidate event fired", 1, listener.invalidateEvents);
+    }
+
+    /**
+     * Tests whether invalidate() performs correct synchronization.
+     */
+    @Test
+    public void testInvalidateSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        config.invalidate();
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     /**
@@ -826,274 +958,142 @@ public class TestCombinedConfiguration {
     }
 
     /**
-     * Tests concurrent read and write access on a combined configuration. There are multiple reader threads and a single
-     * writer thread. It is checked that no inconsistencies occur.
+     * Tests removing a configuration.
      */
     @Test
-    public void testConcurrentAccess() throws ConfigurationException, InterruptedException {
-        // populate the test combined configuration
-        setUpSourceTest();
-        final XMLConfiguration xmlConf = new XMLConfiguration();
-        new FileHandler(xmlConf).load(ConfigurationAssert.getTestFile("test.xml"));
-        config.addConfiguration(xmlConf);
-        final PropertiesConfiguration propConf = new PropertiesConfiguration();
-        new FileHandler(propConf).load(ConfigurationAssert.getTestFile("test.properties"));
-        for (int i = 0; i < 8; i++) {
-            config.addConfiguration(new BaseHierarchicalConfiguration());
-        }
-        config.getConfiguration(0).addProperty(KEY_CONCURRENT, TEST_NAME);
-
-        // Set a single synchronizer for all involved configurations
-        final Synchronizer sync = new ReadWriteSynchronizer();
-        config.setSynchronizer(sync);
-        for (final Configuration c : config.getConfigurations()) {
-            c.setSynchronizer(sync);
-        }
-
-        // setup test threads
-        final int numberOfReaders = 3;
-        final int readCount = 5000;
-        final int writeCount = 3000;
-        final CountDownLatch latch = new CountDownLatch(1);
-        final AtomicInteger errorCount = new AtomicInteger();
-        final Collection<Thread> threads = new ArrayList<>(numberOfReaders + 1);
-        final Thread writeThread = new WriteThread(config, latch, errorCount, writeCount);
-        writeThread.start();
-        threads.add(writeThread);
-        for (int i = 0; i < numberOfReaders; i++) {
-            final Thread readThread = new ReadThread(config, latch, errorCount, readCount);
-            readThread.start();
-            threads.add(readThread);
-        }
-
-        // perform test
-        latch.countDown();
-        for (final Thread t : threads) {
-            t.join();
-        }
-        assertEquals("Got errors", 0, errorCount.get());
+    public void testRemoveConfiguration() {
+        final AbstractConfiguration c = setUpTestConfiguration();
+        config.addConfiguration(c);
+        checkAddConfig(c);
+        assertTrue("Config could not be removed", config.removeConfiguration(c));
+        checkRemoveConfig(c);
     }
 
     /**
-     * Prepares the test configuration for a test for sub configurations. Some child configurations are added.
-     *
-     * @return the sub configuration at the test sub key
+     * Tests removing a configuration by index.
      */
-    private AbstractConfiguration setUpSubConfigTest() {
-        final AbstractConfiguration srcConfig = setUpTestConfiguration();
-        config.addConfiguration(srcConfig, "source", SUB_KEY);
-        config.addConfiguration(setUpTestConfiguration());
-        config.addConfiguration(setUpTestConfiguration(), "otherTest", "other.prefix");
-        return srcConfig;
+    @Test
+    public void testRemoveConfigurationAt() {
+        final AbstractConfiguration c = setUpTestConfiguration();
+        config.addConfiguration(c);
+        assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
+        checkRemoveConfig(c);
     }
 
     /**
-     * Tests whether a sub configuration survives updates of its parent.
+     * Tests removing a configuration by name.
      */
     @Test
-    public void testSubConfigurationWithUpdates() {
-        final AbstractConfiguration srcConfig = setUpSubConfigTest();
-        final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt(SUB_KEY, true);
-        assertTrue("Wrong value before update", sub.getBoolean(TEST_KEY));
-        srcConfig.setProperty(TEST_KEY, Boolean.FALSE);
-        assertFalse("Wrong value after update", sub.getBoolean(TEST_KEY));
-        assertFalse("Wrong value from combined configuration", config.getBoolean(SUB_KEY + '.' + TEST_KEY));
+    public void testRemoveConfigurationByName() {
+        final AbstractConfiguration c = setUpTestConfiguration();
+        config.addConfiguration(c, TEST_NAME);
+        assertSame("Wrong config removed", c, config.removeConfiguration(TEST_NAME));
+        checkRemoveConfig(c);
     }
 
     /**
-     * Checks the configurationsAt() method.
-     *
-     * @param withUpdates flag whether updates are supported
+     * Tests removing a configuration by name, which is not contained.
      */
-    private void checkConfigurationsAt(final boolean withUpdates) {
-        setUpSubConfigTest();
-        final List<HierarchicalConfiguration<ImmutableNode>> subs = config.configurationsAt(SUB_KEY, withUpdates);
-        assertEquals("Wrong number of sub configurations", 1, subs.size());
-        assertTrue("Wrong value in sub configuration", subs.get(0).getBoolean(TEST_KEY));
+    @Test
+    public void testRemoveConfigurationByUnknownName() {
+        assertNull("Could remove configuration by unknown name", config.removeConfiguration("unknownName"));
+        listener.checkEvent(0, 0);
     }
 
     /**
-     * Tests whether sub configurations can be created from a key.
+     * Tests removing a configuration with a name.
      */
     @Test
-    public void testConfigurationsAt() {
-        checkConfigurationsAt(false);
+    public void testRemoveNamedConfiguration() {
+        final AbstractConfiguration c = setUpTestConfiguration();
+        config.addConfiguration(c, TEST_NAME);
+        config.removeConfiguration(c);
+        checkRemoveConfig(c);
     }
 
     /**
-     * Tests whether sub configurations can be created which are attached.
+     * Tests removing a named configuration by index.
      */
     @Test
-    public void testConfigurationsAtWithUpdates() {
-        checkConfigurationsAt(true);
+    public void testRemoveNamedConfigurationAt() {
+        final AbstractConfiguration c = setUpTestConfiguration();
+        config.addConfiguration(c, TEST_NAME);
+        assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
+        checkRemoveConfig(c);
     }
 
     /**
-     * Helper method for creating a test configuration to be added to the combined configuration.
-     *
-     * @return the test configuration
+     * Tests removing a configuration that was not added prior.
      */
-    private static AbstractConfiguration setUpTestConfiguration() {
-        final BaseHierarchicalConfiguration config = new BaseHierarchicalConfiguration();
-        config.addProperty(TEST_KEY, Boolean.TRUE);
-        config.addProperty("test.comment", "This is a test");
-        return config;
+    @Test
+    public void testRemoveNonContainedConfiguration() {
+        assertFalse("Could remove non contained config", config.removeConfiguration(setUpTestConfiguration()));
+        listener.checkEvent(0, 0);
     }
 
     /**
-     * Test event listener class for checking if the expected invalidate events are fired.
+     * Tests whether write access to the conversion expression engine is synchronized.
      */
-    private static class CombinedListener implements EventListener<ConfigurationEvent> {
-        int invalidateEvents;
-
-        int otherEvents;
-
-        @Override
-        public void onEvent(final ConfigurationEvent event) {
-            if (event.getEventType() == CombinedConfiguration.COMBINED_INVALIDATE) {
-                invalidateEvents++;
-            } else {
-                otherEvents++;
-            }
-        }
-
-        /**
-         * Checks if the expected number of events was fired.
-         *
-         * @param expectedInvalidate the expected number of invalidate events
-         * @param expectedOthers the expected number of other events
-         */
-        public void checkEvent(final int expectedInvalidate, final int expectedOthers) {
-            assertEquals("Wrong number of invalidate events", expectedInvalidate, invalidateEvents);
-            assertEquals("Wrong number of other events", expectedOthers, otherEvents);
-        }
+    @Test
+    public void testSetConversionExpressionEngineSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        config.setConversionExpressionEngine(new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS));
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+        checkCombinedRootNotConstructed();
     }
 
     /**
-     * A test thread performing reads on a combined configuration. This thread reads a certain property from the
-     * configuration. If everything works well, this property should have at least one and at most two values.
+     * Tests if setting a node combiner causes an invalidation.
      */
-    private static class ReadThread extends Thread {
-        /** The configuration to be accessed. */
-        private final Configuration config;
-
-        /** The latch for synchronizing thread start. */
-        private final CountDownLatch startLatch;
-
-        /** A counter for read errors. */
-        private final AtomicInteger errorCount;
-
-        /** The number of reads to be performed. */
-        private final int numberOfReads;
-
-        /**
-         * Creates a new instance of {@code ReadThread}.
-         *
-         * @param readConfig the configuration to be read
-         * @param latch the latch for synchronizing thread start
-         * @param errCnt the counter for read errors
-         * @param readCount the number of reads to be performed
-         */
-        public ReadThread(final Configuration readConfig, final CountDownLatch latch, final AtomicInteger errCnt, final int readCount) {
-            config = readConfig;
-            startLatch = latch;
-            errorCount = errCnt;
-            numberOfReads = readCount;
-        }
-
-        /**
-         * Reads from the test configuration.
-         */
-        @Override
-        public void run() {
-            try {
-                startLatch.await();
-                for (int i = 0; i < numberOfReads; i++) {
-                    readConfiguration();
-                }
-            } catch (final Exception e) {
-                errorCount.incrementAndGet();
-            }
-        }
-
-        /**
-         * Reads the test property from the associated configuration. Its values are checked.
-         */
-        private void readConfiguration() {
-            final List<Object> values = config.getList(KEY_CONCURRENT);
-            if (values.size() < 1 || values.size() > 2) {
-                errorCount.incrementAndGet();
-            } else {
-                boolean ok = true;
-                for (final Object value : values) {
-                    if (!TEST_NAME.equals(value)) {
-                        ok = false;
-                    }
-                }
-                if (!ok) {
-                    errorCount.incrementAndGet();
-                }
-            }
-        }
+    @Test
+    public void testSetNodeCombiner() {
+        final NodeCombiner combiner = new UnionCombiner();
+        config.setNodeCombiner(combiner);
+        assertSame("Node combiner was not set", combiner, config.getNodeCombiner());
+        listener.checkEvent(1, 0);
     }
 
     /**
-     * A test thread performing updates on a test configuration. This thread modifies configurations which are children of a
-     * combined configuration. Each update operation adds a value to one of the child configurations and removes it from
-     * another one (which contained it before). So if concurrent reads are performed, the test property should always have
-     * between 1 and 2 values.
+     * Tests whether setNodeCombiner() is correctly synchronized.
      */
-    private static class WriteThread extends Thread {
-        /** The list with the child configurations. */
-        private final List<Configuration> testConfigs;
-
-        /** The latch for synchronizing thread start. */
-        private final CountDownLatch startLatch;
-
-        /** A counter for errors. */
-        private final AtomicInteger errorCount;
-
-        /** The number of write operations to be performed. */
-        private final int numberOfWrites;
-
-        /** The index of the child configuration containing the test property. */
-        private int currentChildConfigIdx;
+    @Test
+    public void testSetNodeCombinerSynchronized() {
+        final SynchronizerTestImpl sync = setUpSynchronizerTest();
+        config.setNodeCombiner(new UnionCombiner());
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
+        checkCombinedRootNotConstructed();
+    }
 
-        /**
-         * Creates a new instance of {@code WriteThread}.
-         *
-         * @param cc the test combined configuration
-         * @param latch the latch for synchronizing test start
-         * @param errCnt a counter for errors
-         * @param writeCount the number of writes to be performed
-         */
-        public WriteThread(final CombinedConfiguration cc, final CountDownLatch latch, final AtomicInteger errCnt, final int writeCount) {
-            testConfigs = cc.getConfigurations();
-            startLatch = latch;
-            errorCount = errCnt;
-            numberOfWrites = writeCount;
-        }
+    /**
+     * Tests setting a null node combiner. This should cause an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetNullNodeCombiner() {
+        config.setNodeCombiner(null);
+    }
 
-        @Override
-        public void run() {
-            try {
-                startLatch.await();
-                for (int i = 0; i < numberOfWrites; i++) {
-                    updateConfigurations();
-                }
-            } catch (final InterruptedException e) {
-                errorCount.incrementAndGet();
-            }
-        }
+    /**
+     * Tests whether a sub configuration survives updates of its parent.
+     */
+    @Test
+    public void testSubConfigurationWithUpdates() {
+        final AbstractConfiguration srcConfig = setUpSubConfigTest();
+        final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt(SUB_KEY, true);
+        assertTrue("Wrong value before update", sub.getBoolean(TEST_KEY));
+        srcConfig.setProperty(TEST_KEY, Boolean.FALSE);
+        assertFalse("Wrong value after update", sub.getBoolean(TEST_KEY));
+        assertFalse("Wrong value from combined configuration", config.getBoolean(SUB_KEY + '.' + TEST_KEY));
+    }
 
-        /**
-         * Performs the update operation.
-         */
-        private void updateConfigurations() {
-            final int newIdx = (currentChildConfigIdx + 1) % testConfigs.size();
-            testConfigs.get(newIdx).addProperty(KEY_CONCURRENT, TEST_NAME);
-            testConfigs.get(currentChildConfigIdx).clearProperty(KEY_CONCURRENT);
-            currentChildConfigIdx = newIdx;
-        }
+    /**
+     * Tests if an update of a contained configuration leeds to an invalidation of the combined configuration.
+     */
+    @Test
+    public void testUpdateContainedConfiguration() {
+        final AbstractConfiguration c = setUpTestConfiguration();
+        config.addConfiguration(c);
+        c.addProperty("test.otherTest", "yes");
+        assertEquals("New property not found", "yes", config.getString("test.otherTest"));
+        listener.checkEvent(2, 0);
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestCompositeConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestCompositeConfiguration.java
index e7d5662..256197c 100644
--- a/src/test/java/org/apache/commons/configuration2/TestCompositeConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestCompositeConfiguration.java
@@ -66,6 +66,47 @@ public class TestCompositeConfiguration {
     private final String testProperties2 = ConfigurationAssert.getTestFile("test2.properties").getAbsolutePath();
     private final String testPropertiesXML = ConfigurationAssert.getTestFile("test.xml").getAbsolutePath();
 
+    /**
+     * Helper method for testing whether the list delimiter is correctly handled.
+     */
+    private void checkSetListDelimiterHandler() {
+        cc.addProperty("test.list", "a/b/c");
+        cc.addProperty("test.property", "a,b,c");
+        assertEquals("Wrong number of list elements", 3, cc.getList("test.list").size());
+        assertEquals("Wrong value of property", "a,b,c", cc.getString("test.property"));
+
+        final AbstractConfiguration config = (AbstractConfiguration) cc.getInMemoryConfiguration();
+        final DefaultListDelimiterHandler listHandler = (DefaultListDelimiterHandler) config.getListDelimiterHandler();
+        assertEquals("Wrong list delimiter", '/', listHandler.getDelimiter());
+    }
+
+    /**
+     * Creates a test synchronizer and installs it at the test configuration.
+     *
+     * @return the test synchronizer
+     */
+    private SynchronizerTestImpl installSynchronizer() {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(conf2);
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        cc.setSynchronizer(sync);
+        return sync;
+    }
+
+    /**
+     * Prepares a test for interpolation with multiple configurations and similar properties.
+     */
+    private void prepareInterpolationTest() {
+        final PropertiesConfiguration p = new PropertiesConfiguration();
+        p.addProperty("foo", "initial");
+        p.addProperty("bar", "${foo}");
+        p.addProperty("prefix.foo", "override");
+
+        cc.addConfiguration(p.subset("prefix"));
+        cc.addConfiguration(p);
+        assertEquals("Wrong value on direct access", "override", cc.getString("bar"));
+    }
+
     @Before
     public void setUp() throws Exception {
         cc = new CompositeConfiguration();
@@ -87,23 +128,22 @@ public class TestCompositeConfiguration {
         cc.setThrowExceptionOnMissing(true);
     }
 
-    @Test
-    public void testThrowExceptionOnMissing() {
-        assertTrue("Throw Exception Property is not set!", cc.isThrowExceptionOnMissing());
+    /**
+     * Prepares a test of the getSource() method.
+     */
+    private void setUpSourceTest() {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(conf2);
     }
 
+    /**
+     * Tests whether adding a child configuration is synchronized.
+     */
     @Test
-    public void testAddRemoveConfigurations() throws Exception {
-        cc.addConfiguration(conf1);
-        assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
-        cc.addConfiguration(conf1);
-        assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
-        cc.addConfiguration(conf2);
-        assertEquals("Number of configurations", 3, cc.getNumberOfConfigurations());
-        cc.removeConfiguration(conf1);
-        assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
-        cc.clear();
-        assertEquals("Number of configurations", 1, cc.getNumberOfConfigurations());
+    public void testAddConfigurationSynchronized() {
+        final SynchronizerTestImpl sync = installSynchronizer();
+        cc.addConfiguration(xmlConf);
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     @Test
@@ -120,30 +160,41 @@ public class TestCompositeConfiguration {
         assertEquals("Number of configurations", 1, cc.getNumberOfConfigurations());
     }
 
+    /**
+     * Tests adding values. Make sure they _DON'T_ override any other properties but add to the existing properties and keep
+     * sequence
+     */
     @Test
-    public void testGetPropertyWIncludes() throws Exception {
+    public void testAddingProperty() throws Exception {
         cc.addConfiguration(conf1);
-        cc.addConfiguration(conf2);
-        final List<Object> l = cc.getList("packages");
-        assertTrue(l.contains("packagea"));
+        cc.addConfiguration(xmlConf);
+
+        String[] values = cc.getStringArray("test.short");
+
+        assertEquals("Number of values before add is wrong!", 1, values.length);
+        assertEquals("First Value before add is wrong", "1", values[0]);
+
+        cc.addProperty("test.short", "88");
+
+        values = cc.getStringArray("test.short");
+
+        assertEquals("Number of values is wrong!", 2, values.length);
+        assertEquals("First Value is wrong", "1", values[0]);
+        assertEquals("Third Value is wrong", "88", values[1]);
     }
 
     @Test
-    public void testGetProperty() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(conf2);
-        assertEquals("Make sure we get the property from conf1 first", "test.properties", cc.getString("propertyInOrder"));
-        cc.clear();
-
-        cc.addConfiguration(conf2);
+    public void testAddRemoveConfigurations() throws Exception {
         cc.addConfiguration(conf1);
-        assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
-        cc.clear();
-
+        assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
         cc.addConfiguration(conf1);
-        cc.addConfigurationFirst(conf2);
-        assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
+        assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
+        cc.addConfiguration(conf2);
+        assertEquals("Number of configurations", 3, cc.getNumberOfConfigurations());
+        cc.removeConfiguration(conf1);
+        assertEquals("Number of configurations", 2, cc.getNumberOfConfigurations());
         cc.clear();
+        assertEquals("Number of configurations", 1, cc.getNumberOfConfigurations());
     }
 
     @Test
@@ -158,40 +209,81 @@ public class TestCompositeConfiguration {
     }
 
     @Test
-    public void testGetPropertyMissing() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(conf2);
-        try {
-            assertNull(cc.getString("bogus.property"));
-            fail("Should have thrown a NoSuchElementException");
-        } catch (final NoSuchElementException nsee) {
-            assertTrue(nsee.getMessage().contains("bogus.property"));
+    public void testCheckingInMemoryConfiguration() throws Exception {
+        final String TEST_KEY = "testKey";
+        final Configuration defaults = new PropertiesConfiguration();
+        defaults.setProperty(TEST_KEY, "testValue");
+        final Configuration testConfiguration = new CompositeConfiguration(defaults);
+        assertTrue(testConfiguration.containsKey(TEST_KEY));
+        assertFalse(testConfiguration.isEmpty());
+        boolean foundTestKey = false;
+        final Iterator<String> i = testConfiguration.getKeys();
+        // assertTrue(i instanceof IteratorChain);
+        // IteratorChain ic = (IteratorChain)i;
+        // assertEquals(2,i.size());
+        for (; i.hasNext();) {
+            final String key = i.next();
+            if (key.equals(TEST_KEY)) {
+                foundTestKey = true;
+            }
         }
-
-        assertTrue("Should be false", !cc.getBoolean("test.missing.boolean", false));
-        assertTrue("Should be true", cc.getBoolean("test.missing.boolean.true", true));
+        assertTrue(foundTestKey);
+        testConfiguration.clearProperty(TEST_KEY);
+        assertFalse(testConfiguration.containsKey(TEST_KEY));
     }
 
     /**
-     * Tests {@code List} parsing.
+     * Tests setting values. These are set in memory mode only!
      */
     @Test
-    public void testMultipleTypesOfConfigs() throws Exception {
+    public void testClearingProperty() throws Exception {
         cc.addConfiguration(conf1);
         cc.addConfiguration(xmlConf);
-        assertEquals("Make sure we get the property from conf1 first", 1, cc.getInt("test.short"));
-        cc.clear();
+        cc.clearProperty("test.short");
+        assertTrue("Make sure test.short is gone!", !cc.containsKey("test.short"));
+    }
 
-        cc.addConfiguration(xmlConf);
-        cc.addConfiguration(conf1);
-        assertEquals("Make sure we get the property from xml", 8, cc.getInt("test.short"));
+    @Test
+    public void testClone() {
+        final CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
+        assertEquals("Wrong number of contained configurations", cc.getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
+
+        final StrictConfigurationComparator comp = new StrictConfigurationComparator();
+        for (int i = 0; i < cc.getNumberOfConfigurations(); i++) {
+            assertEquals("Wrong configuration class at " + i, cc.getConfiguration(i).getClass(), cc2.getConfiguration(i).getClass());
+            assertNotSame("Configuration was not cloned", cc.getConfiguration(i), cc2.getConfiguration(i));
+            assertTrue("Configurations at " + i + " not equal", comp.compare(cc.getConfiguration(i), cc2.getConfiguration(i)));
+        }
+
+        assertTrue("Configurations are not equal", comp.compare(cc, cc2));
     }
 
+    /**
+     * Ensures that event listeners are not cloned.
+     */
     @Test
-    public void testPropertyExistsInOnlyOneConfig() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-        assertEquals("value", cc.getString("element"));
+    public void testCloneEventListener() {
+        cc.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
+        final CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
+        assertTrue("Listeners have been cloned", cc2.getEventListeners(ConfigurationEvent.ANY).isEmpty());
+    }
+
+    /**
+     * Tests whether interpolation works as expected after cloning.
+     */
+    @Test
+    public void testCloneInterpolation() {
+        final CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
+        assertNotSame("Interpolator was not cloned", cc.getInterpolator(), cc2.getInterpolator());
+    }
+
+    /**
+     * Tests cloning if one of the contained configurations does not support this operation. This should cause an exception.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testCloneNotSupported() {
+        cc.addConfiguration(new NonCloneableConfiguration());
+        cc.clone();
     }
 
     /**
@@ -206,129 +298,107 @@ public class TestCompositeConfiguration {
         assertTrue(1.4 == cc.getDouble("bogus", 1.4));
     }
 
+    /**
+     * Tests whether add property events are triggered.
+     */
     @Test
-    public void testGettingConfiguration() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-        assertEquals(PropertiesConfiguration.class, cc.getConfiguration(0).getClass());
-        assertEquals(XMLConfiguration.class, cc.getConfiguration(1).getClass());
+    public void testEventAddProperty() {
+        final EventListenerTestImpl listener = new EventListenerTestImpl(cc);
+        cc.addEventListener(ConfigurationEvent.ANY, listener);
+        cc.addProperty("test", "value");
+        listener.checkEvent(ConfigurationEvent.ADD_PROPERTY, "test", "value", true);
+        listener.checkEvent(ConfigurationEvent.ADD_PROPERTY, "test", "value", false);
+        listener.done();
     }
 
     /**
-     * Tests setting values. These are set in memory mode only!
+     * Tests whether clear property events are triggered.
      */
     @Test
-    public void testClearingProperty() throws Exception {
+    public void testEventClearProperty() {
         cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-        cc.clearProperty("test.short");
-        assertTrue("Make sure test.short is gone!", !cc.containsKey("test.short"));
+        final String key = "configuration.loaded";
+        assertTrue("Wrong value for property", cc.getBoolean(key));
+        final EventListenerTestImpl listener = new EventListenerTestImpl(cc);
+        cc.addEventListener(ConfigurationEvent.ANY, listener);
+        cc.clearProperty(key);
+        assertFalse("Key still present", cc.containsKey(key));
+        listener.checkEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, true);
+        listener.checkEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, false);
+        listener.done();
     }
 
     /**
-     * Tests adding values. Make sure they _DON'T_ override any other properties but add to the existing properties and keep
-     * sequence
+     * Tests whether set property events are triggered.
      */
     @Test
-    public void testAddingProperty() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-
-        String[] values = cc.getStringArray("test.short");
-
-        assertEquals("Number of values before add is wrong!", 1, values.length);
-        assertEquals("First Value before add is wrong", "1", values[0]);
-
-        cc.addProperty("test.short", "88");
-
-        values = cc.getStringArray("test.short");
-
-        assertEquals("Number of values is wrong!", 2, values.length);
-        assertEquals("First Value is wrong", "1", values[0]);
-        assertEquals("Third Value is wrong", "88", values[1]);
+    public void testEventSetProperty() {
+        final EventListenerTestImpl listener = new EventListenerTestImpl(cc);
+        cc.addEventListener(ConfigurationEvent.ANY, listener);
+        cc.setProperty("test", "value");
+        listener.checkEvent(ConfigurationEvent.SET_PROPERTY, "test", "value", true);
+        listener.checkEvent(ConfigurationEvent.SET_PROPERTY, "test", "value", false);
+        listener.done();
     }
 
     /**
-     * Tests setting values. These are set in memory mode only!
+     * Tests whether access to a configuration by index is synchronized.
      */
     @Test
-    public void testSettingMissingProperty() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-        cc.setProperty("my.new.property", "supernew");
-        assertEquals("supernew", cc.getString("my.new.property"));
-    }
-
-    /**
-     * Tests retrieving subsets of configurations
-     */
-    @Test
-    public void testGettingSubset() throws Exception {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-
-        Configuration subset = cc.subset("test");
-        assertNotNull(subset);
-        assertFalse("Shouldn't be empty", subset.isEmpty());
-        assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "1", subset.getString("short"));
-
-        cc.setProperty("test.short", "43");
-        subset = cc.subset("test");
-        assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "43", subset.getString("short"));
+    public void testGetConfigurationSynchronized() {
+        final SynchronizerTestImpl sync = installSynchronizer();
+        assertEquals("Wrong result", conf1, cc.getConfiguration(0));
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
-     * Tests subsets and still can resolve elements
+     * Tests whether access to the in-memory configuration is synchronized.
      */
     @Test
-    public void testSubsetCanResolve() throws Exception {
-        cc = new CompositeConfiguration();
-        final BaseConfiguration config = new BaseConfiguration();
-        config.addProperty("subset.tempfile", "${java.io.tmpdir}/file.tmp");
-        cc.addConfiguration(config);
-        cc.addConfiguration(ConfigurationConverter.getConfiguration(System.getProperties()));
-
-        final Configuration subset = cc.subset("subset");
-        assertEquals(System.getProperty("java.io.tmpdir") + "/file.tmp", subset.getString("tempfile"));
+    public void testGetInMemoryConfigurationSynchronized() {
+        final SynchronizerTestImpl sync = installSynchronizer();
+        cc.getInMemoryConfiguration();
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
     }
 
     /**
-     * Tests {@code List} parsing.
+     * Tests {@code getKeys(String key)} preserves the order
      */
     @Test
-    public void testList() throws Exception {
+    public void testGetKeys2PreservesOrder() throws Exception {
         cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-
-        List<Object> packages = cc.getList("packages");
-        // we should get 3 packages here
-        assertEquals(3, packages.size());
-
-        final List<Object> defaultList = new ArrayList<>();
-        defaultList.add("1");
-        defaultList.add("2");
-
-        packages = cc.getList("packages.which.dont.exist", defaultList);
-        // we should get 2 packages here
-        assertEquals(2, packages.size());
-
+        final List<String> orderedList = new ArrayList<>();
+        for (final Iterator<String> keys = conf1.getKeys("test"); keys.hasNext();) {
+            orderedList.add(keys.next());
+        }
+        final List<String> iteratedList = new ArrayList<>();
+        for (final Iterator<String> keys = cc.getKeys("test"); keys.hasNext();) {
+            iteratedList.add(keys.next());
+        }
+        assertEquals(orderedList.size(), iteratedList.size());
+        for (int i = 0; i < orderedList.size(); i++) {
+            assertEquals(orderedList.get(i), iteratedList.get(i));
+        }
     }
 
     /**
-     * Tests {@code String} array parsing.
+     * Tests {@code getKeys} preserves the order
      */
     @Test
-    public void testStringArray() throws Exception {
+    public void testGetKeysPreservesOrder() throws Exception {
         cc.addConfiguration(conf1);
-        cc.addConfiguration(xmlConf);
-
-        String[] packages = cc.getStringArray("packages");
-        // we should get 3 packages here
-        assertEquals(3, packages.length);
-
-        packages = cc.getStringArray("packages.which.dont.exist");
-        // we should get 0 packages here
-        assertEquals(0, packages.length);
+        final List<String> orderedList = new ArrayList<>();
+        for (final Iterator<String> keys = conf1.getKeys(); keys.hasNext();) {
+            orderedList.add(keys.next());
+        }
+        final List<String> iteratedList = new ArrayList<>();
+        for (final Iterator<String> keys = cc.getKeys(); keys.hasNext();) {
+            iteratedList.add(keys.next());
+        }
+        assertEquals(orderedList.size(), iteratedList.size());
+        for (int i = 0; i < orderedList.size(); i++) {
+            assertEquals(orderedList.get(i), iteratedList.get(i));
+        }
     }
 
     @Test
@@ -364,43 +434,125 @@ public class TestCompositeConfiguration {
     }
 
     /**
-     * Tests {@code getKeys} preserves the order
+     * Tests querying a list when a tricky interpolation is involved. This is related to CONFIGURATION-339.
      */
     @Test
-    public void testGetKeysPreservesOrder() throws Exception {
-        cc.addConfiguration(conf1);
-        final List<String> orderedList = new ArrayList<>();
-        for (final Iterator<String> keys = conf1.getKeys(); keys.hasNext();) {
-            orderedList.add(keys.next());
-        }
-        final List<String> iteratedList = new ArrayList<>();
-        for (final Iterator<String> keys = cc.getKeys(); keys.hasNext();) {
-            iteratedList.add(keys.next());
-        }
-        assertEquals(orderedList.size(), iteratedList.size());
-        for (int i = 0; i < orderedList.size(); i++) {
-            assertEquals(orderedList.get(i), iteratedList.get(i));
-        }
+    public void testGetListWithInterpolation() {
+        prepareInterpolationTest();
+        final List<Object> lst = cc.getList("bar");
+        assertEquals("Wrong number of values", 1, lst.size());
+        assertEquals("Wrong value in list", "override", lst.get(0));
     }
 
     /**
-     * Tests {@code getKeys(String key)} preserves the order
+     * Tests whether querying the number of child configurations is synchronized.
      */
     @Test
-    public void testGetKeys2PreservesOrder() throws Exception {
+    public void testGetNumberOfConfigurationsSynchronized() {
+        final SynchronizerTestImpl sync = installSynchronizer();
+        assertEquals("Wrong number of configurations", 3, cc.getNumberOfConfigurations());
+        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
+    }
+
+    @Test
+    public void testGetProperty() throws Exception {
         cc.addConfiguration(conf1);
-        final List<String> orderedList = new ArrayList<>();
-        for (final Iterator<String> keys = conf1.getKeys("test"); keys.hasNext();) {
-            orderedList.add(keys.next());
-        }
-        final List<String> iteratedList = new ArrayList<>();
-        for (final Iterator<String> keys = cc.getKeys("test"); keys.hasNext();) {
-            iteratedList.add(keys.next());
-        }
-        assertEquals(orderedList.size(), iteratedList.size());
-        for (int i = 0; i < orderedList.size(); i++) {
-            assertEquals(orderedList.get(i), iteratedList.get(i));
+        cc.addConfiguration(conf2);
+        assertEquals("Make sure we get the property from conf1 first", "test.properties", cc.getString("propertyInOrder"));
+        cc.clear();
+
+        cc.addConfiguration(conf2);
+        cc.addConfiguration(conf1);
+        assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
+        cc.clear();
+
+        cc.addConfiguration(conf1);
+        cc.addConfigurationFirst(conf2);
+        assertEquals("Make sure we get the property from conf2 first", "test2.properties", cc.getString("propertyInOrder"));
+        cc.clear();
+    }
+
+    @Test
+    public void testGetPropertyMissing() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(conf2);
+        try {
+            assertNull(cc.getString("bogus.property"));
+            fail("Should have thrown a NoSuchElementException");
+        } catch (final NoSuchElementException nsee) {
+            assertTrue(nsee.getMessage().contains("bogus.property"));
         }
+
+        assertTrue("Should be false", !cc.getBoolean("test.missing.boolean", false));
+        assertTrue("Should be true", cc.getBoolean("test.missing.boolean.true", true));
+    }
+
+    @Test
+    public void testGetPropertyWIncludes() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(conf2);
+        final List<Object> l = cc.getList("packages");
+        assertTrue(l.contains("packagea"));
+    }
+
+    /**
+     * Tests the getSource() method for a property contained in the in memory configuration.
+     */
+    @Test
+    public void testGetSourceInMemory() {
+        setUpSourceTest();
+        cc.addProperty(TEST_PROPERTY, Boolean.TRUE);
+        assertSame("Source not found in in-memory config", cc.getInMemoryConfiguration(), cc.getSource(TEST_PROPERTY));
+    }
+
+    /**
+     * Tests the getSource() method if the property is defined by multiple child configurations. In this case an exception
+     * should be thrown.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetSourceMultiple() {
+        setUpSourceTest();
+        conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
+        cc.addProperty(TEST_PROPERTY, "a value");
+        cc.getSource(TEST_PROPERTY);
+    }
+
+    /**
+     * Tests the getSource() method for a null key. This should cause an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetSourceNull() {
+        cc.getSource(null);
+    }
+
+    /**
+     * Tests the getSource() method if the property is defined in a single child configuration.
+     */
+    @Test
+    public void testGetSourceSingle() {
+        setUpSourceTest();
+        conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
+        assertSame("Wrong source configuration", conf1, cc.getSource(TEST_PROPERTY));
+    }
+
+    /**
+     * Tests the getSource() method for an unknown property key.
+     */
+    @Test
+    public void testGetSourceUnknown() {
+        setUpSourceTest();
+        assertNull("Wrong source for unknown key", cc.getSource(TEST_PROPERTY));
+    }
+
+    /**
+     * Tests querying a string array when a tricky interpolation is involved.
+     */
+    @Test
+    public void testGetStringArrayWithInterpolation() {
+        prepareInterpolationTest();
+        final String[] values = cc.getStringArray("bar");
+        assertEquals("Wrong number of values", 1, values.length);
+        assertEquals("Wrong value in array", "override", values[0]);
     }
 
     @Test
@@ -430,60 +582,29 @@ public class TestCompositeConfiguration {
     }
 
     @Test
-    public void testCheckingInMemoryConfiguration() throws Exception {
-        final String TEST_KEY = "testKey";
-        final Configuration defaults = new PropertiesConfiguration();
-        defaults.setProperty(TEST_KEY, "testValue");
-        final Configuration testConfiguration = new CompositeConfiguration(defaults);
-        assertTrue(testConfiguration.containsKey(TEST_KEY));
-        assertFalse(testConfiguration.isEmpty());
-        boolean foundTestKey = false;
-        final Iterator<String> i = testConfiguration.getKeys();
-        // assertTrue(i instanceof IteratorChain);
-        // IteratorChain ic = (IteratorChain)i;
-        // assertEquals(2,i.size());
-        for (; i.hasNext();) {
-            final String key = i.next();
-            if (key.equals(TEST_KEY)) {
-                foundTestKey = true;
-            }
-        }
-        assertTrue(foundTestKey);
-        testConfiguration.clearProperty(TEST_KEY);
-        assertFalse(testConfiguration.containsKey(TEST_KEY));
-    }
-
-    @Test
-    public void testStringArrayInterpolation() {
-        final CompositeConfiguration config = new CompositeConfiguration();
-        config.addProperty("base", "foo");
-        config.addProperty("list", "${base}.bar1");
-        config.addProperty("list", "${base}.bar2");
-        config.addProperty("list", "${base}.bar3");
-
-        final String[] array = config.getStringArray("list");
-        assertEquals("size", 3, array.length);
-        assertEquals("1st element", "foo.bar1", array[0]);
-        assertEquals("2nd element", "foo.bar2", array[1]);
-        assertEquals("3rd element", "foo.bar3", array[2]);
+    public void testGettingConfiguration() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
+        assertEquals(PropertiesConfiguration.class, cc.getConfiguration(0).getClass());
+        assertEquals(XMLConfiguration.class, cc.getConfiguration(1).getClass());
     }
 
     /**
-     * Tests whether global interpolation works with lists.
+     * Tests retrieving subsets of configurations
      */
     @Test
-    public void testListInterpolation() {
-        final PropertiesConfiguration c1 = new PropertiesConfiguration();
-        c1.addProperty("c1.value", "test1");
-        c1.addProperty("c1.value", "${c2.value}");
-        cc.addConfiguration(c1);
-        final PropertiesConfiguration c2 = new PropertiesConfiguration();
-        c2.addProperty("c2.value", "test2");
-        cc.addConfiguration(c2);
-        final List<Object> lst = cc.getList("c1.value");
-        assertEquals("Wrong list size", 2, lst.size());
-        assertEquals("Wrong first element", "test1", lst.get(0));
-        assertEquals("Wrong second element", "test2", lst.get(1));
+    public void testGettingSubset() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
+
+        Configuration subset = cc.subset("test");
+        assertNotNull(subset);
+        assertFalse("Shouldn't be empty", subset.isEmpty());
+        assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "1", subset.getString("short"));
+
+        cc.setProperty("test.short", "43");
+        subset = cc.subset("test");
+        assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "43", subset.getString("short"));
     }
 
     @Test
@@ -498,90 +619,121 @@ public class TestCompositeConfiguration {
         assertTrue("The in memory configuration is not empty", config.getInMemoryConfiguration().isEmpty());
     }
 
+    /**
+     * Tests whether interpolation works if a variable references a property with multiple values. This test is related to
+     * CONFIGURATION-632.
+     */
     @Test
-    public void testClone() {
-        final CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
-        assertEquals("Wrong number of contained configurations", cc.getNumberOfConfigurations(), cc2.getNumberOfConfigurations());
-
-        final StrictConfigurationComparator comp = new StrictConfigurationComparator();
-        for (int i = 0; i < cc.getNumberOfConfigurations(); i++) {
-            assertEquals("Wrong configuration class at " + i, cc.getConfiguration(i).getClass(), cc2.getConfiguration(i).getClass());
-            assertNotSame("Configuration was not cloned", cc.getConfiguration(i), cc2.getConfiguration(i));
-            assertTrue("Configurations at " + i + " not equal", comp.compare(cc.getConfiguration(i), cc2.getConfiguration(i)));
-        }
-
-        assertTrue("Configurations are not equal", comp.compare(cc, cc2));
+    public void testInterpolationArrayReference() {
+        final Configuration props = new PropertiesConfiguration();
+        final String[] values = {"a", "property", "with", "multiple", "values"};
+        props.addProperty("keyMultiValues", values);
+        props.addProperty("keyReference", "${keyMultiValues}");
+        cc.addConfiguration(props);
+        assertArrayEquals("Wrong interpolated value", values, cc.getStringArray("keyReference"));
     }
 
     /**
-     * Tests cloning if one of the contained configurations does not support this operation. This should cause an exception.
+     * Tests whether interpolation works if multiple configurations are involved. This test is related to CONFIGURATION-441.
      */
-    @Test(expected = ConfigurationRuntimeException.class)
-    public void testCloneNotSupported() {
-        cc.addConfiguration(new NonCloneableConfiguration());
-        cc.clone();
+    @Test
+    public void testInterpolationInMultipleConfigs() {
+        final Configuration c1 = new PropertiesConfiguration();
+        c1.addProperty("property.one", "one");
+        c1.addProperty("property.two", "two");
+        final Configuration c2 = new PropertiesConfiguration();
+        c2.addProperty("property.one.ref", "${property.one}");
+        cc.addConfiguration(c1);
+        cc.addConfiguration(c2);
+        assertEquals("Wrong interpolated value", "one", cc.getString("property.one.ref"));
     }
 
     /**
-     * Ensures that event listeners are not cloned.
+     * Tests {@code List} parsing.
      */
     @Test
-    public void testCloneEventListener() {
-        cc.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
-        final CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
-        assertTrue("Listeners have been cloned", cc2.getEventListeners(ConfigurationEvent.ANY).isEmpty());
+    public void testList() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
+
+        List<Object> packages = cc.getList("packages");
+        // we should get 3 packages here
+        assertEquals(3, packages.size());
+
+        final List<Object> defaultList = new ArrayList<>();
+        defaultList.add("1");
+        defaultList.add("2");
+
+        packages = cc.getList("packages.which.dont.exist", defaultList);
+        // we should get 2 packages here
+        assertEquals(2, packages.size());
+
     }
 
     /**
-     * Tests whether interpolation works as expected after cloning.
+     * Tests whether global interpolation works with lists.
      */
     @Test
-    public void testCloneInterpolation() {
-        final CompositeConfiguration cc2 = (CompositeConfiguration) cc.clone();
-        assertNotSame("Interpolator was not cloned", cc.getInterpolator(), cc2.getInterpolator());
+    public void testListInterpolation() {
+        final PropertiesConfiguration c1 = new PropertiesConfiguration();
+        c1.addProperty("c1.value", "test1");
+        c1.addProperty("c1.value", "${c2.value}");
+        cc.addConfiguration(c1);
+        final PropertiesConfiguration c2 = new PropertiesConfiguration();
+        c2.addProperty("c2.value", "test2");
+        cc.addConfiguration(c2);
+        final List<Object> lst = cc.getList("c1.value");
+        assertEquals("Wrong list size", 2, lst.size());
+        assertEquals("Wrong first element", "test1", lst.get(0));
+        assertEquals("Wrong second element", "test2", lst.get(1));
     }
 
     /**
-     * Tests whether add property events are triggered.
+     * Tests {@code List} parsing.
      */
     @Test
-    public void testEventAddProperty() {
-        final EventListenerTestImpl listener = new EventListenerTestImpl(cc);
-        cc.addEventListener(ConfigurationEvent.ANY, listener);
-        cc.addProperty("test", "value");
-        listener.checkEvent(ConfigurationEvent.ADD_PROPERTY, "test", "value", true);
-        listener.checkEvent(ConfigurationEvent.ADD_PROPERTY, "test", "value", false);
-        listener.done();
+    public void testMultipleTypesOfConfigs() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
+        assertEquals("Make sure we get the property from conf1 first", 1, cc.getInt("test.short"));
+        cc.clear();
+
+        cc.addConfiguration(xmlConf);
+        cc.addConfiguration(conf1);
+        assertEquals("Make sure we get the property from xml", 8, cc.getInt("test.short"));
+    }
+
+    @Test
+    public void testPropertyExistsInOnlyOneConfig() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
+        assertEquals("value", cc.getString("element"));
     }
 
     /**
-     * Tests whether set property events are triggered.
+     * Tests whether removing a child configuration is synchronized.
      */
     @Test
-    public void testEventSetProperty() {
-        final EventListenerTestImpl listener = new EventListenerTestImpl(cc);
-        cc.addEventListener(ConfigurationEvent.ANY, listener);
-        cc.setProperty("test", "value");
-        listener.checkEvent(ConfigurationEvent.SET_PROPERTY, "test", "value", true);
-        listener.checkEvent(ConfigurationEvent.SET_PROPERTY, "test", "value", false);
-        listener.done();
+    public void testRemoveConfigurationSynchronized() {
+        final SynchronizerTestImpl sync = installSynchronizer();
+        cc.removeConfiguration(conf1);
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
     }
 
     /**
-     * Tests whether clear property events are triggered.
+     * Tests whether the in-memory configuration can be replaced by a new child configuration.
      */
     @Test
-    public void testEventClearProperty() {
-        cc.addConfiguration(conf1);
-        final String key = "configuration.loaded";
-        assertTrue("Wrong value for property", cc.getBoolean(key));
-        final EventListenerTestImpl listener = new EventListenerTestImpl(cc);
-        cc.addEventListener(ConfigurationEvent.ANY, listener);
-        cc.clearProperty(key);
-        assertFalse("Key still present", cc.containsKey(key));
-        listener.checkEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, true);
-        listener.checkEvent(ConfigurationEvent.CLEAR_PROPERTY, key, null, false);
-        listener.done();
+    public void testReplaceInMemoryConfig() {
+        conf1.setProperty(TEST_PROPERTY, "conf1");
+        conf2.setProperty(TEST_PROPERTY, "conf2");
+        cc.addConfiguration(conf1, true);
+        cc.addProperty("newProperty1", "newValue1");
+        cc.addConfiguration(conf2, true);
+        cc.addProperty("newProperty2", "newValue2");
+        assertEquals("Wrong property", "conf1", cc.getString(TEST_PROPERTY));
+        assertEquals("Not added to in-memory config", "newValue1", conf1.getString("newProperty1"));
+        assertEquals("In-memory config not changed", "newValue2", conf2.getString("newProperty2"));
     }
 
     /**
@@ -604,151 +756,78 @@ public class TestCompositeConfiguration {
     }
 
     /**
-     * Helper method for testing whether the list delimiter is correctly handled.
-     */
-    private void checkSetListDelimiterHandler() {
-        cc.addProperty("test.list", "a/b/c");
-        cc.addProperty("test.property", "a,b,c");
-        assertEquals("Wrong number of list elements", 3, cc.getList("test.list").size());
-        assertEquals("Wrong value of property", "a,b,c", cc.getString("test.property"));
-
-        final AbstractConfiguration config = (AbstractConfiguration) cc.getInMemoryConfiguration();
-        final DefaultListDelimiterHandler listHandler = (DefaultListDelimiterHandler) config.getListDelimiterHandler();
-        assertEquals("Wrong list delimiter", '/', listHandler.getDelimiter());
-    }
-
-    /**
-     * Prepares a test of the getSource() method.
-     */
-    private void setUpSourceTest() {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(conf2);
-    }
-
-    /**
-     * Tests the getSource() method if the property is defined in a single child configuration.
+     * Tests the behavior of setListDelimiterHandler() if the in-memory configuration is not derived from BaseConfiguration.
+     * This test is related to CONFIGURATION-476.
      */
     @Test
-    public void testGetSourceSingle() {
-        setUpSourceTest();
-        conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
-        assertSame("Wrong source configuration", conf1, cc.getSource(TEST_PROPERTY));
+    public void testSetListDelimiterInMemoryConfigNonBaseConfig() {
+        final Configuration inMemoryConfig = EasyMock.createMock(Configuration.class);
+        EasyMock.replay(inMemoryConfig);
+        cc = new CompositeConfiguration(inMemoryConfig);
+        cc.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
     }
 
     /**
-     * Tests the getSource() method for an unknown property key.
+     * Tests setting values. These are set in memory mode only!
      */
     @Test
-    public void testGetSourceUnknown() {
-        setUpSourceTest();
-        assertNull("Wrong source for unknown key", cc.getSource(TEST_PROPERTY));
+    public void testSettingMissingProperty() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
+        cc.setProperty("my.new.property", "supernew");
+        assertEquals("supernew", cc.getString("my.new.property"));
     }
 
     /**
-     * Tests the getSource() method for a property contained in the in memory configuration.
+     * Tests {@code String} array parsing.
      */
     @Test
-    public void testGetSourceInMemory() {
-        setUpSourceTest();
-        cc.addProperty(TEST_PROPERTY, Boolean.TRUE);
-        assertSame("Source not found in in-memory config", cc.getInMemoryConfiguration(), cc.getSource(TEST_PROPERTY));
-    }
-
-    /**
-     * Tests the getSource() method if the property is defined by multiple child configurations. In this case an exception
-     * should be thrown.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetSourceMultiple() {
-        setUpSourceTest();
-        conf1.addProperty(TEST_PROPERTY, Boolean.TRUE);
-        cc.addProperty(TEST_PROPERTY, "a value");
-        cc.getSource(TEST_PROPERTY);
-    }
-
-    /**
-     * Tests the getSource() method for a null key. This should cause an exception.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetSourceNull() {
-        cc.getSource(null);
-    }
+    public void testStringArray() throws Exception {
+        cc.addConfiguration(conf1);
+        cc.addConfiguration(xmlConf);
 
-    /**
-     * Prepares a test for interpolation with multiple configurations and similar properties.
-     */
-    private void prepareInterpolationTest() {
-        final PropertiesConfiguration p = new PropertiesConfiguration();
-        p.addProperty("foo", "initial");
-        p.addProperty("bar", "${foo}");
-        p.addProperty("prefix.foo", "override");
+        String[] packages = cc.getStringArray("packages");
+        // we should get 3 packages here
+        assertEquals(3, packages.length);
 
-        cc.addConfiguration(p.subset("prefix"));
-        cc.addConfiguration(p);
-        assertEquals("Wrong value on direct access", "override", cc.getString("bar"));
+        packages = cc.getStringArray("packages.which.dont.exist");
+        // we should get 0 packages here
+        assertEquals(0, packages.length);
     }
 
-    /**
-     * Tests querying a list when a tricky interpolation is involved. This is related to CONFIGURATION-339.
-     */
     @Test
-    public void testGetListWithInterpolation() {
-        prepareInterpolationTest();
-        final List<Object> lst = cc.getList("bar");
-        assertEquals("Wrong number of values", 1, lst.size());
-        assertEquals("Wrong value in list", "override", lst.get(0));
-    }
+    public void testStringArrayInterpolation() {
+        final CompositeConfiguration config = new CompositeConfiguration();
+        config.addProperty("base", "foo");
+        config.addProperty("list", "${base}.bar1");
+        config.addProperty("list", "${base}.bar2");
+        config.addProperty("list", "${base}.bar3");
 
-    /**
-     * Tests querying a string array when a tricky interpolation is involved.
-     */
-    @Test
-    public void testGetStringArrayWithInterpolation() {
-        prepareInterpolationTest();
-        final String[] values = cc.getStringArray("bar");
-        assertEquals("Wrong number of values", 1, values.length);
-        assertEquals("Wrong value in array", "override", values[0]);
+        final String[] array = config.getStringArray("list");
+        assertEquals("size", 3, array.length);
+        assertEquals("1st element", "foo.bar1", array[0]);
+        assertEquals("2nd element", "foo.bar2", array[1]);
+        assertEquals("3rd element", "foo.bar3", array[2]);
     }
 
     /**
-     * Tests whether interpolation works if multiple configurations are involved. This test is related to CONFIGURATION-441.
+     * Tests subsets and still can resolve elements
      */
     @Test
-    public void testInterpolationInMultipleConfigs() {
-        final Configuration c1 = new PropertiesConfiguration();
-        c1.addProperty("property.one", "one");
-        c1.addProperty("property.two", "two");
-        final Configuration c2 = new PropertiesConfiguration();
-        c2.addProperty("property.one.ref", "${property.one}");
-        cc.addConfiguration(c1);
-        cc.addConfiguration(c2);
-        assertEquals("Wrong interpolated value", "one", cc.getString("property.one.ref"));
-    }
+    public void testSubsetCanResolve() throws Exception {
+        cc = new CompositeConfiguration();
+        final BaseConfiguration config = new BaseConfiguration();
+        config.addProperty("subset.tempfile", "${java.io.tmpdir}/file.tmp");
+        cc.addConfiguration(config);
+        cc.addConfiguration(ConfigurationConverter.getConfiguration(System.getProperties()));
 
-    /**
-     * Tests whether interpolation works if a variable references a property with multiple values. This test is related to
-     * CONFIGURATION-632.
-     */
-    @Test
-    public void testInterpolationArrayReference() {
-        final Configuration props = new PropertiesConfiguration();
-        final String[] values = {"a", "property", "with", "multiple", "values"};
-        props.addProperty("keyMultiValues", values);
-        props.addProperty("keyReference", "${keyMultiValues}");
-        cc.addConfiguration(props);
-        assertArrayEquals("Wrong interpolated value", values, cc.getStringArray("keyReference"));
+        final Configuration subset = cc.subset("subset");
+        assertEquals(System.getProperty("java.io.tmpdir") + "/file.tmp", subset.getString("tempfile"));
     }
 
-    /**
-     * Tests the behavior of setListDelimiterHandler() if the in-memory configuration is not derived from BaseConfiguration.
-     * This test is related to CONFIGURATION-476.
-     */
     @Test
-    public void testSetListDelimiterInMemoryConfigNonBaseConfig() {
-        final Configuration inMemoryConfig = EasyMock.createMock(Configuration.class);
-        EasyMock.replay(inMemoryConfig);
-        cc = new CompositeConfiguration(inMemoryConfig);
-        cc.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
+    public void testThrowExceptionOnMissing() {
+        assertTrue("Throw Exception Property is not set!", cc.isThrowExceptionOnMissing());
     }
 
     /**
@@ -766,83 +845,4 @@ public class TestCompositeConfiguration {
         cc.addProperty("newProperty", "newValue");
         assertEquals("Not added to in-memory config", "newValue", conf1.getString("newProperty"));
     }
-
-    /**
-     * Tests whether the in-memory configuration can be replaced by a new child configuration.
-     */
-    @Test
-    public void testReplaceInMemoryConfig() {
-        conf1.setProperty(TEST_PROPERTY, "conf1");
-        conf2.setProperty(TEST_PROPERTY, "conf2");
-        cc.addConfiguration(conf1, true);
-        cc.addProperty("newProperty1", "newValue1");
-        cc.addConfiguration(conf2, true);
-        cc.addProperty("newProperty2", "newValue2");
-        assertEquals("Wrong property", "conf1", cc.getString(TEST_PROPERTY));
-        assertEquals("Not added to in-memory config", "newValue1", conf1.getString("newProperty1"));
-        assertEquals("In-memory config not changed", "newValue2", conf2.getString("newProperty2"));
-    }
-
-    /**
-     * Creates a test synchronizer and installs it at the test configuration.
-     *
-     * @return the test synchronizer
-     */
-    private SynchronizerTestImpl installSynchronizer() {
-        cc.addConfiguration(conf1);
-        cc.addConfiguration(conf2);
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        cc.setSynchronizer(sync);
-        return sync;
-    }
-
-    /**
-     * Tests whether adding a child configuration is synchronized.
-     */
-    @Test
-    public void testAddConfigurationSynchronized() {
-        final SynchronizerTestImpl sync = installSynchronizer();
-        cc.addConfiguration(xmlConf);
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
-    }
-
-    /**
-     * Tests whether removing a child configuration is synchronized.
-     */
-    @Test
-    public void testRemoveConfigurationSynchronized() {
-        final SynchronizerTestImpl sync = installSynchronizer();
-        cc.removeConfiguration(conf1);
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
-    }
-
-    /**
-     * Tests whether access to a configuration by index is synchronized.
-     */
-    @Test
-    public void testGetConfigurationSynchronized() {
-        final SynchronizerTestImpl sync = installSynchronizer();
-        assertEquals("Wrong result", conf1, cc.getConfiguration(0));
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-    }
-
-    /**
-     * Tests whether access to the in-memory configuration is synchronized.
-     */
-    @Test
-    public void testGetInMemoryConfigurationSynchronized() {
-        final SynchronizerTestImpl sync = installSynchronizer();
-        cc.getInMemoryConfiguration();
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-    }
-
-    /**
-     * Tests whether querying the number of child configurations is synchronized.
-     */
-    @Test
-    public void testGetNumberOfConfigurationsSynchronized() {
-        final SynchronizerTestImpl sync = installSynchronizer();
-        assertEquals("Wrong number of configurations", 3, cc.getNumberOfConfigurations());
-        sync.verify(Methods.BEGIN_READ, Methods.END_READ);
-    }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestConfigurationConverter.java b/src/test/java/org/apache/commons/configuration2/TestConfigurationConverter.java
index 0757868..8064a23 100644
--- a/src/test/java/org/apache/commons/configuration2/TestConfigurationConverter.java
+++ b/src/test/java/org/apache/commons/configuration2/TestConfigurationConverter.java
@@ -33,23 +33,6 @@ import org.junit.Test;
  *
  */
 public class TestConfigurationConverter {
-    @Test
-    public void testPropertiesToConfiguration() {
-        final Properties props = new Properties();
-        props.setProperty("string", "teststring");
-        props.setProperty("int", "123");
-        props.setProperty("list", "item 1, item 2");
-
-        final AbstractConfiguration config = (AbstractConfiguration) ConfigurationConverter.getConfiguration(props);
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-
-        assertEquals("This returns 'teststring'", "teststring", config.getString("string"));
-        final List<Object> item1 = config.getList("list");
-        assertEquals("This returns 'item 1'", "item 1", item1.get(0));
-
-        assertEquals("This returns 123", 123, config.getInt("int"));
-    }
-
     /**
      * Creates a configuration object with some test values.
      *
@@ -66,6 +49,17 @@ public class TestConfigurationConverter {
         return config;
     }
 
+    @Test
+    public void testConfigurationToMap() {
+        final Configuration config = new BaseConfiguration();
+        config.addProperty("string", "teststring");
+
+        final Map<Object, Object> map = ConfigurationConverter.getMap(config);
+
+        assertNotNull("null map", map);
+        assertEquals("'string' property", "teststring", map.get("string"));
+    }
+
     /**
      * Tests a conversion to Properties if the default list delimiter handler is used (which does not support list joining).
      */
@@ -124,14 +118,20 @@ public class TestConfigurationConverter {
     }
 
     @Test
-    public void testConfigurationToMap() {
-        final Configuration config = new BaseConfiguration();
-        config.addProperty("string", "teststring");
+    public void testPropertiesToConfiguration() {
+        final Properties props = new Properties();
+        props.setProperty("string", "teststring");
+        props.setProperty("int", "123");
+        props.setProperty("list", "item 1, item 2");
 
-        final Map<Object, Object> map = ConfigurationConverter.getMap(config);
+        final AbstractConfiguration config = (AbstractConfiguration) ConfigurationConverter.getConfiguration(props);
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
 
-        assertNotNull("null map", map);
-        assertEquals("'string' property", "teststring", map.get("string"));
+        assertEquals("This returns 'teststring'", "teststring", config.getString("string"));
+        final List<Object> item1 = config.getList("list");
+        assertEquals("This returns 'item 1'", "item 1", item1.get(0));
+
+        assertEquals("This returns 123", 123, config.getInt("int"));
     }
 
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestConfigurationLookup.java b/src/test/java/org/apache/commons/configuration2/TestConfigurationLookup.java
index 2f0b662..4ac20c1 100644
--- a/src/test/java/org/apache/commons/configuration2/TestConfigurationLookup.java
+++ b/src/test/java/org/apache/commons/configuration2/TestConfigurationLookup.java
@@ -44,14 +44,22 @@ public class TestConfigurationLookup {
     }
 
     /**
-     * Tests whether an existing variable can be resolved.
+     * Tests lookup() for a complex property value.
      */
     @Test
-    public void testLookupSuccess() {
+    public void testLookupComplex() {
+        final int count = 5;
         final Configuration conf = new BaseConfiguration();
-        conf.addProperty(VAR, VALUE);
+        for (int i = 0; i < count; i++) {
+            conf.addProperty(VAR, String.valueOf(VALUE) + i);
+        }
         final ConfigurationLookup lookup = new ConfigurationLookup(conf);
-        assertEquals("Wrong result", VALUE, lookup.lookup(VAR));
+        final Collection<?> col = (Collection<?>) lookup.lookup(VAR);
+        assertEquals("Wrong number of elements", count, col.size());
+        final Iterator<?> it = col.iterator();
+        for (int i = 0; i < count; i++) {
+            assertEquals("Wrong element at " + i, String.valueOf(VALUE) + i, it.next());
+        }
     }
 
     /**
@@ -76,21 +84,13 @@ public class TestConfigurationLookup {
     }
 
     /**
-     * Tests lookup() for a complex property value.
+     * Tests whether an existing variable can be resolved.
      */
     @Test
-    public void testLookupComplex() {
-        final int count = 5;
+    public void testLookupSuccess() {
         final Configuration conf = new BaseConfiguration();
-        for (int i = 0; i < count; i++) {
-            conf.addProperty(VAR, String.valueOf(VALUE) + i);
-        }
+        conf.addProperty(VAR, VALUE);
         final ConfigurationLookup lookup = new ConfigurationLookup(conf);
-        final Collection<?> col = (Collection<?>) lookup.lookup(VAR);
-        assertEquals("Wrong number of elements", count, col.size());
-        final Iterator<?> it = col.iterator();
-        for (int i = 0; i < count; i++) {
-            assertEquals("Wrong element at " + i, String.valueOf(VALUE) + i, it.next());
-        }
+        assertEquals("Wrong result", VALUE, lookup.lookup(VAR));
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestConfigurationSet.java b/src/test/java/org/apache/commons/configuration2/TestConfigurationSet.java
index f9ab06f..cbd194e 100644
--- a/src/test/java/org/apache/commons/configuration2/TestConfigurationSet.java
+++ b/src/test/java/org/apache/commons/configuration2/TestConfigurationSet.java
@@ -58,11 +58,6 @@ public class TestConfigurationSet {
         set = null;
     }
 
-    @Test
-    public void testSize() {
-        assertEquals("Entry set does not match properties size.", properties.length, set.size());
-    }
-
     /**
      * Class under test for Iterator iterator()
      */
@@ -83,4 +78,9 @@ public class TestConfigurationSet {
         }
         assertTrue("Iterator failed to remove all properties.", set.isEmpty());
     }
+
+    @Test
+    public void testSize() {
+        assertEquals("Entry set does not match properties size.", properties.length, set.size());
+    }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestConfigurationUtils.java b/src/test/java/org/apache/commons/configuration2/TestConfigurationUtils.java
index 19171d4..e8c976c 100644
--- a/src/test/java/org/apache/commons/configuration2/TestConfigurationUtils.java
+++ b/src/test/java/org/apache/commons/configuration2/TestConfigurationUtils.java
@@ -54,6 +54,43 @@ import org.junit.Test;
  *
  */
 public class TestConfigurationUtils {
+    /**
+     * A test Synchronizer implementation which can be cloned.
+     */
+    private static class CloneableSynchronizer extends NonCloneableSynchronizer implements Cloneable {
+        /** A flag whether clone() was called. */
+        private final boolean cloned;
+
+        /**
+         * Creates a new instance of {@code CloneableSynchronizer} and sets the clone flag.
+         *
+         * @param clone the clone flag
+         */
+        public CloneableSynchronizer(final boolean clone) {
+            cloned = clone;
+        }
+
+        @Override
+        public Object clone() {
+            return new CloneableSynchronizer(true);
+        }
+
+        /**
+         * Returns a flag whether this object was cloned.
+         *
+         * @return the clone flag
+         */
+        public boolean isCloned() {
+            return cloned;
+        }
+    }
+
+    /**
+     * A test Synchronizer implementation which cannot be cloned.
+     */
+    private static class NonCloneableSynchronizer extends SynchronizerTestImpl {
+    }
+
     /** Constant for the name of a class to be loaded. */
     private static final String CLS_NAME = "org.apache.commons.configuration2.PropertiesConfiguration";
 
@@ -71,45 +108,6 @@ public class TestConfigurationUtils {
     }
 
     @Test
-    public void testToString() {
-        final Configuration config = new BaseConfiguration();
-        final String lineSeparator = System.getProperty("line.separator");
-
-        assertEquals("String representation of an empty configuration", "", ConfigurationUtils.toString(config));
-
-        config.setProperty("one", "1");
-        assertEquals("String representation of a configuration", "one=1", ConfigurationUtils.toString(config));
-
-        config.setProperty("two", "2");
-        assertEquals("String representation of a configuration", "one=1" + lineSeparator + "two=2", ConfigurationUtils.toString(config));
-
-        config.clearProperty("one");
-        assertEquals("String representation of a configuration", "two=2", ConfigurationUtils.toString(config));
-
-        config.setProperty("one", "1");
-        assertEquals("String representation of a configuration", "two=2" + lineSeparator + "one=1", ConfigurationUtils.toString(config));
-    }
-
-    @Test
-    public void testCopy() {
-        // create the source configuration
-        final Configuration conf1 = new BaseConfiguration();
-        conf1.addProperty("key1", "value1");
-        conf1.addProperty("key2", "value2");
-
-        // create the target configuration
-        final Configuration conf2 = new BaseConfiguration();
-        conf2.addProperty("key1", "value3");
-        conf2.addProperty("key2", "value4");
-
-        // copy the source configuration into the target configuration
-        ConfigurationUtils.copy(conf1, conf2);
-
-        assertEquals("'key1' property", "value1", conf2.getProperty("key1"));
-        assertEquals("'key2' property", "value2", conf2.getProperty("key2"));
-    }
-
-    @Test
     public void testAppend() {
         // create the source configuration
         final Configuration conf1 = new BaseConfiguration();
@@ -136,24 +134,160 @@ public class TestConfigurationUtils {
     }
 
     /**
-     * Tests converting a configuration into a hierarchical one.
+     * Tests asEventSource() if an exception is expected.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testAsEventSourceNonSupportedEx() {
+        ConfigurationUtils.asEventSource(this, false);
+    }
+
+    /**
+     * Tests asEventSource() if the passed in object implements this interface.
      */
     @Test
-    public void testConvertToHierarchical() {
-        final Configuration conf = new BaseConfiguration();
-        for (int i = 0; i < 10; i++) {
-            conf.addProperty("test" + i, "value" + i);
-            conf.addProperty("test.list", "item" + i);
-        }
+    public void testAsEventSourceSupported() {
+        final XMLConfiguration src = new XMLConfiguration();
+        assertSame("Wrong result", src, ConfigurationUtils.asEventSource(src, true));
+    }
 
-        final BaseHierarchicalConfiguration hc = (BaseHierarchicalConfiguration) ConfigurationUtils.convertToHierarchical(conf);
-        for (final Iterator<String> it = conf.getKeys(); it.hasNext();) {
-            final String key = it.next();
-            assertEquals("Wrong value for key " + key, conf.getProperty(key), hc.getProperty(key));
+    /**
+     * Tests asEventSource() if a mock object has to be returned.
+     */
+    @Test
+    public void testAsEventSourceUnsupportedMock() {
+        @SuppressWarnings("unchecked")
+        final EventListener<ConfigurationEvent> cl = EasyMock.createMock(EventListener.class);
+        EasyMock.replay(cl);
+        final EventSource source = ConfigurationUtils.asEventSource(this, true);
+        source.addEventListener(ConfigurationEvent.ANY, cl);
+        assertFalse("Wrong result (1)", source.removeEventListener(ConfigurationEvent.ANY, cl));
+        source.addEventListener(ConfigurationEvent.ANY, null);
+    }
+
+    /**
+     * Tests cloning a configuration that supports this operation.
+     */
+    @Test
+    public void testCloneConfiguration() {
+        final BaseHierarchicalConfiguration conf = new BaseHierarchicalConfiguration();
+        conf.addProperty("test", "yes");
+        final BaseHierarchicalConfiguration copy = (BaseHierarchicalConfiguration) ConfigurationUtils.cloneConfiguration(conf);
+        assertNotSame("Same object was returned", conf, copy);
+        assertEquals("Property was not cloned", "yes", copy.getString("test"));
+    }
+
+    /**
+     * Tests cloning a configuration that does not support this operation. This should cause an exception.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testCloneConfigurationNotSupported() {
+        final Configuration myNonCloneableConfig = new NonCloneableConfiguration();
+        ConfigurationUtils.cloneConfiguration(myNonCloneableConfig);
+    }
+
+    /**
+     * Tests cloning a <b>null</b> configuration.
+     */
+    @Test
+    public void testCloneConfigurationNull() {
+        assertNull("Wrong return value", ConfigurationUtils.cloneConfiguration(null));
+    }
+
+    /**
+     * Tests whether errors are handled correctly by cloneIfPossible().
+     */
+    @Test
+    public void testCloneIfPossibleError() {
+        final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl() {
+            @Override
+            public XMLBuilderParametersImpl clone() {
+                throw new ConfigurationRuntimeException();
+            }
+        };
+        assertSame("Wrong result", params, ConfigurationUtils.cloneIfPossible(params));
+    }
+
+    /**
+     * Tests cloneIfPossible() if the passed in object does not support cloning.
+     */
+    @Test
+    public void testCloneIfPossibleNotSupported() {
+        final Long value = 20130116221714L;
+        assertSame("Wrong result", value, ConfigurationUtils.cloneIfPossible(value));
+    }
+
+    /**
+     * Tests whether cloneIfPossible() can handle null parameters.
+     */
+    @Test
+    public void testCloneIfPossibleNull() {
+        assertNull("Wrong result", ConfigurationUtils.cloneIfPossible(null));
+    }
+
+    /**
+     * Tests whether an object can be cloned which supports cloning.
+     */
+    @Test
+    public void testCloneIfPossibleSupported() {
+        final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl();
+        params.setPublicID("testID");
+        params.setSchemaValidation(true);
+        final XMLBuilderParametersImpl clone = (XMLBuilderParametersImpl) ConfigurationUtils.cloneIfPossible(params);
+        assertNotSame("No clone was created", params, clone);
+        final Map<String, Object> map = clone.getParameters();
+        for (final Map.Entry<String, Object> e : params.getParameters().entrySet()) {
+            if (!e.getKey().startsWith("config-")) {
+                assertEquals("Wrong value for field " + e.getKey(), e.getValue(), map.get(e.getKey()));
+            }
         }
     }
 
     /**
+     * Tests whether a Synchronizer can be cloned using its clone() method.
+     */
+    @Test
+    public void testCloneSynchronizerClone() {
+        final CloneableSynchronizer sync = new CloneableSynchronizer(false);
+        final CloneableSynchronizer sync2 = (CloneableSynchronizer) ConfigurationUtils.cloneSynchronizer(sync);
+        assertTrue("Not cloned", sync2.isCloned());
+    }
+
+    /**
+     * Tests cloneSynchronizer() if the argument cannot be cloned.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testCloneSynchronizerFailed() {
+        ConfigurationUtils.cloneSynchronizer(new NonCloneableSynchronizer());
+    }
+
+    /**
+     * Tests whether a new Synchronizer can be created using reflection.
+     */
+    @Test
+    public void testCloneSynchronizerNewInstance() {
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        final SynchronizerTestImpl sync2 = (SynchronizerTestImpl) ConfigurationUtils.cloneSynchronizer(sync);
+        assertNotNull("Clone is null", sync2);
+        assertNotSame("Same instance", sync, sync2);
+    }
+
+    /**
+     * Tests whether the NoOpSyhnchronizer can be cloned.
+     */
+    @Test
+    public void testCloneSynchronizerNoOp() {
+        assertSame("Wrong result", NoOpSynchronizer.INSTANCE, ConfigurationUtils.cloneSynchronizer(NoOpSynchronizer.INSTANCE));
+    }
+
+    /**
+     * Tries to clone a null Synchronizer.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testCloneSynchronizerNull() {
+        ConfigurationUtils.cloneSynchronizer(null);
+    }
+
+    /**
      * Tests converting a configuration into a hierarchical one that is already hierarchical.
      */
     @Test
@@ -164,6 +298,30 @@ public class TestConfigurationUtils {
     }
 
     /**
+     * Tests converting an already hierarchical configuration using an expression engine. The new engine should be set.
+     */
+    @Test
+    public void testConvertHierarchicalToHierarchicalEngine() {
+        final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
+        final ExpressionEngine engine = new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS);
+        assertSame("Created new configuration", hc, ConfigurationUtils.convertToHierarchical(hc, engine));
+        assertSame("Engine was not set", engine, hc.getExpressionEngine());
+    }
+
+    /**
+     * Tests converting an already hierarchical configuration using a null expression engine. In this case the expression
+     * engine of the configuration should not be touched.
+     */
+    @Test
+    public void testConvertHierarchicalToHierarchicalNullEngine() {
+        final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
+        final ExpressionEngine engine = new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS);
+        hc.setExpressionEngine(engine);
+        assertSame("Created new configuration", hc, ConfigurationUtils.convertToHierarchical(hc, null));
+        assertSame("Expression engine was changed", engine, hc.getExpressionEngine());
+    }
+
+    /**
      * Tests converting a null configuration to a hierarchical one. The result should be null, too.
      */
     @Test
@@ -172,6 +330,24 @@ public class TestConfigurationUtils {
     }
 
     /**
+     * Tests converting a configuration into a hierarchical one.
+     */
+    @Test
+    public void testConvertToHierarchical() {
+        final Configuration conf = new BaseConfiguration();
+        for (int i = 0; i < 10; i++) {
+            conf.addProperty("test" + i, "value" + i);
+            conf.addProperty("test.list", "item" + i);
+        }
+
+        final BaseHierarchicalConfiguration hc = (BaseHierarchicalConfiguration) ConfigurationUtils.convertToHierarchical(conf);
+        for (final Iterator<String> it = conf.getKeys(); it.hasNext();) {
+            final String key = it.next();
+            assertEquals("Wrong value for key " + key, conf.getProperty(key), hc.getProperty(key));
+        }
+    }
+
+    /**
      * Tests converting a configuration into a hierarchical one if some of its properties contain escaped list delimiter
      * characters.
      */
@@ -201,30 +377,6 @@ public class TestConfigurationUtils {
     }
 
     /**
-     * Tests converting an already hierarchical configuration using an expression engine. The new engine should be set.
-     */
-    @Test
-    public void testConvertHierarchicalToHierarchicalEngine() {
-        final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
-        final ExpressionEngine engine = new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS);
-        assertSame("Created new configuration", hc, ConfigurationUtils.convertToHierarchical(hc, engine));
-        assertSame("Engine was not set", engine, hc.getExpressionEngine());
-    }
-
-    /**
-     * Tests converting an already hierarchical configuration using a null expression engine. In this case the expression
-     * engine of the configuration should not be touched.
-     */
-    @Test
-    public void testConvertHierarchicalToHierarchicalNullEngine() {
-        final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
-        final ExpressionEngine engine = new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS);
-        hc.setExpressionEngine(engine);
-        assertSame("Created new configuration", hc, ConfigurationUtils.convertToHierarchical(hc, null));
-        assertSame("Expression engine was changed", engine, hc.getExpressionEngine());
-    }
-
-    /**
      * Tests converting a configuration to a hierarchical one that contains a property with multiple values. This test is
      * related to CONFIGURATION-346.
      */
@@ -255,82 +407,23 @@ public class TestConfigurationUtils {
         assertEquals("Wrong number of children of x", 1, nodeX.getChildren().size());
     }
 
-    /**
-     * Tests cloning a configuration that supports this operation.
-     */
-    @Test
-    public void testCloneConfiguration() {
-        final BaseHierarchicalConfiguration conf = new BaseHierarchicalConfiguration();
-        conf.addProperty("test", "yes");
-        final BaseHierarchicalConfiguration copy = (BaseHierarchicalConfiguration) ConfigurationUtils.cloneConfiguration(conf);
-        assertNotSame("Same object was returned", conf, copy);
-        assertEquals("Property was not cloned", "yes", copy.getString("test"));
-    }
-
-    /**
-     * Tests cloning a configuration that does not support this operation. This should cause an exception.
-     */
-    @Test(expected = ConfigurationRuntimeException.class)
-    public void testCloneConfigurationNotSupported() {
-        final Configuration myNonCloneableConfig = new NonCloneableConfiguration();
-        ConfigurationUtils.cloneConfiguration(myNonCloneableConfig);
-    }
-
-    /**
-     * Tests cloning a <b>null</b> configuration.
-     */
-    @Test
-    public void testCloneConfigurationNull() {
-        assertNull("Wrong return value", ConfigurationUtils.cloneConfiguration(null));
-    }
-
-    /**
-     * Tests whether an object can be cloned which supports cloning.
-     */
     @Test
-    public void testCloneIfPossibleSupported() {
-        final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl();
-        params.setPublicID("testID");
-        params.setSchemaValidation(true);
-        final XMLBuilderParametersImpl clone = (XMLBuilderParametersImpl) ConfigurationUtils.cloneIfPossible(params);
-        assertNotSame("No clone was created", params, clone);
-        final Map<String, Object> map = clone.getParameters();
-        for (final Map.Entry<String, Object> e : params.getParameters().entrySet()) {
-            if (!e.getKey().startsWith("config-")) {
-                assertEquals("Wrong value for field " + e.getKey(), e.getValue(), map.get(e.getKey()));
-            }
-        }
-    }
+    public void testCopy() {
+        // create the source configuration
+        final Configuration conf1 = new BaseConfiguration();
+        conf1.addProperty("key1", "value1");
+        conf1.addProperty("key2", "value2");
 
-    /**
-     * Tests cloneIfPossible() if the passed in object does not support cloning.
-     */
-    @Test
-    public void testCloneIfPossibleNotSupported() {
-        final Long value = 20130116221714L;
-        assertSame("Wrong result", value, ConfigurationUtils.cloneIfPossible(value));
-    }
+        // create the target configuration
+        final Configuration conf2 = new BaseConfiguration();
+        conf2.addProperty("key1", "value3");
+        conf2.addProperty("key2", "value4");
 
-    /**
-     * Tests whether errors are handled correctly by cloneIfPossible().
-     */
-    @Test
-    public void testCloneIfPossibleError() {
-        final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl() {
-            @Override
-            public XMLBuilderParametersImpl clone() {
-                throw new ConfigurationRuntimeException();
-            }
-        };
-        assertSame("Wrong result", params, ConfigurationUtils.cloneIfPossible(params));
-    }
+        // copy the source configuration into the target configuration
+        ConfigurationUtils.copy(conf1, conf2);
 
-    /**
-     * Tests whether cloneIfPossible() can handle null parameters.
-     */
-    @Test
-    public void testCloneIfPossibleNull() {
-        assertNull("Wrong result", ConfigurationUtils.cloneIfPossible(null));
+        assertEquals("'key1' property", "value1", conf2.getProperty("key1"));
+        assertEquals("'key2' property", "value2", conf2.getProperty("key2"));
     }
 
     /**
@@ -370,11 +463,16 @@ public class TestConfigurationUtils {
     }
 
     /**
-     * Tests whether a class can be loaded from CCL.
+     * Tests whether a class can be loaded if it is not found by the CCL.
      */
     @Test
-    public void testLoadClassFromCCL() throws ClassNotFoundException {
-        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+    public void testLoadClassCCLNotFound() throws ClassNotFoundException {
+        Thread.currentThread().setContextClassLoader(new ClassLoader() {
+            @Override
+            public Class<?> loadClass(final String name) throws ClassNotFoundException {
+                throw new ClassNotFoundException(name);
+            }
+        });
         assertEquals("Wrong class", CLS_NAME, ConfigurationUtils.loadClass(CLS_NAME).getName());
     }
 
@@ -388,28 +486,15 @@ public class TestConfigurationUtils {
     }
 
     /**
-     * Tests whether a class can be loaded if it is not found by the CCL.
+     * Tests whether a class can be loaded from CCL.
      */
     @Test
-    public void testLoadClassCCLNotFound() throws ClassNotFoundException {
-        Thread.currentThread().setContextClassLoader(new ClassLoader() {
-            @Override
-            public Class<?> loadClass(final String name) throws ClassNotFoundException {
-                throw new ClassNotFoundException(name);
-            }
-        });
+    public void testLoadClassFromCCL() throws ClassNotFoundException {
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
         assertEquals("Wrong class", CLS_NAME, ConfigurationUtils.loadClass(CLS_NAME).getName());
     }
 
     /**
-     * Tests the behavior of loadClass() for a non-existing class.
-     */
-    @Test(expected = ClassNotFoundException.class)
-    public void testLoadClassNotFound() throws ClassNotFoundException {
-        ConfigurationUtils.loadClass("a non existing class!");
-    }
-
-    /**
      * Tests loadClassNoEx() if the class can be resolved.
      */
     @Test
@@ -426,115 +511,30 @@ public class TestConfigurationUtils {
     }
 
     /**
-     * Tests asEventSource() if the passed in object implements this interface.
-     */
-    @Test
-    public void testAsEventSourceSupported() {
-        final XMLConfiguration src = new XMLConfiguration();
-        assertSame("Wrong result", src, ConfigurationUtils.asEventSource(src, true));
-    }
-
-    /**
-     * Tests asEventSource() if an exception is expected.
-     */
-    @Test(expected = ConfigurationRuntimeException.class)
-    public void testAsEventSourceNonSupportedEx() {
-        ConfigurationUtils.asEventSource(this, false);
-    }
-
-    /**
-     * Tests asEventSource() if a mock object has to be returned.
-     */
-    @Test
-    public void testAsEventSourceUnsupportedMock() {
-        @SuppressWarnings("unchecked")
-        final EventListener<ConfigurationEvent> cl = EasyMock.createMock(EventListener.class);
-        EasyMock.replay(cl);
-        final EventSource source = ConfigurationUtils.asEventSource(this, true);
-        source.addEventListener(ConfigurationEvent.ANY, cl);
-        assertFalse("Wrong result (1)", source.removeEventListener(ConfigurationEvent.ANY, cl));
-        source.addEventListener(ConfigurationEvent.ANY, null);
-    }
-
-    /**
-     * Tries to clone a null Synchronizer.
-     */
-    @Test(expected = IllegalArgumentException.class)
-    public void testCloneSynchronizerNull() {
-        ConfigurationUtils.cloneSynchronizer(null);
-    }
-
-    /**
-     * Tests whether the NoOpSyhnchronizer can be cloned.
-     */
-    @Test
-    public void testCloneSynchronizerNoOp() {
-        assertSame("Wrong result", NoOpSynchronizer.INSTANCE, ConfigurationUtils.cloneSynchronizer(NoOpSynchronizer.INSTANCE));
-    }
-
-    /**
-     * Tests whether a new Synchronizer can be created using reflection.
+     * Tests the behavior of loadClass() for a non-existing class.
      */
-    @Test
-    public void testCloneSynchronizerNewInstance() {
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        final SynchronizerTestImpl sync2 = (SynchronizerTestImpl) ConfigurationUtils.cloneSynchronizer(sync);
-        assertNotNull("Clone is null", sync2);
-        assertNotSame("Same instance", sync, sync2);
+    @Test(expected = ClassNotFoundException.class)
+    public void testLoadClassNotFound() throws ClassNotFoundException {
+        ConfigurationUtils.loadClass("a non existing class!");
     }
 
-    /**
-     * Tests whether a Synchronizer can be cloned using its clone() method.
-     */
     @Test
-    public void testCloneSynchronizerClone() {
-        final CloneableSynchronizer sync = new CloneableSynchronizer(false);
-        final CloneableSynchronizer sync2 = (CloneableSynchronizer) ConfigurationUtils.cloneSynchronizer(sync);
-        assertTrue("Not cloned", sync2.isCloned());
-    }
-
-    /**
-     * Tests cloneSynchronizer() if the argument cannot be cloned.
-     */
-    @Test(expected = ConfigurationRuntimeException.class)
-    public void testCloneSynchronizerFailed() {
-        ConfigurationUtils.cloneSynchronizer(new NonCloneableSynchronizer());
-    }
+    public void testToString() {
+        final Configuration config = new BaseConfiguration();
+        final String lineSeparator = System.getProperty("line.separator");
 
-    /**
-     * A test Synchronizer implementation which cannot be cloned.
-     */
-    private static class NonCloneableSynchronizer extends SynchronizerTestImpl {
-    }
+        assertEquals("String representation of an empty configuration", "", ConfigurationUtils.toString(config));
 
-    /**
-     * A test Synchronizer implementation which can be cloned.
-     */
-    private static class CloneableSynchronizer extends NonCloneableSynchronizer implements Cloneable {
-        /** A flag whether clone() was called. */
-        private final boolean cloned;
+        config.setProperty("one", "1");
+        assertEquals("String representation of a configuration", "one=1", ConfigurationUtils.toString(config));
 
-        /**
-         * Creates a new instance of {@code CloneableSynchronizer} and sets the clone flag.
-         *
-         * @param clone the clone flag
-         */
-        public CloneableSynchronizer(final boolean clone) {
-            cloned = clone;
-        }
+        config.setProperty("two", "2");
+        assertEquals("String representation of a configuration", "one=1" + lineSeparator + "two=2", ConfigurationUtils.toString(config));
 
-        /**
-         * Returns a flag whether this object was cloned.
-         *
-         * @return the clone flag
-         */
-        public boolean isCloned() {
-            return cloned;
-        }
+        config.clearProperty("one");
+        assertEquals("String representation of a configuration", "two=2", ConfigurationUtils.toString(config));
 
-        @Override
-        public Object clone() {
-            return new CloneableSynchronizer(true);
-        }
+        config.setProperty("one", "1");
+        assertEquals("String representation of a configuration", "two=2" + lineSeparator + "one=1", ConfigurationUtils.toString(config));
     }
 }
diff --git a/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java
index 20d9323..532473f 100644
--- a/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java
@@ -57,9 +57,29 @@ public class TestDataConfiguration {
     /** Constant for the date pattern used by tests. */
     private static final String DATE_PATTERN = "yyyy-MM-dd";
 
+    /**
+     * Returns the expected test date.
+     *
+     * @return the expected test date
+     * @throws ParseException if the date cannot be parsed
+     */
+    private static Date expectedDate() throws ParseException {
+        final DateFormat format = new SimpleDateFormat(DATE_PATTERN);
+        return format.parse("2004-01-01");
+    }
+
     /** The test instance. */
     private DataConfiguration conf;
 
+    /**
+     * Create an instance of InternetAddress. This trick is necessary to compile and run the test with Java 1.3 and the
+     * javamail-1.4 which is not compatible with Java 1.3
+     */
+    private Object createInternetAddress(final String email) throws Exception {
+        final Class<?> cls = Class.forName("javax.mail.internet.InternetAddress");
+        return cls.getConstructor(String.class).newInstance(email);
+    }
+
     @Before
     public void setUp() throws Exception {
         final BaseConfiguration baseConfig = new BaseConfiguration();
@@ -338,24 +358,40 @@ public class TestDataConfiguration {
         conf.addProperty("email.object", createInternetAddress("ebourg@apache.org"));
     }
 
+    /**
+     * Tests whether properties can be cleared.
+     */
     @Test
-    public void testGetConfiguration() {
-        final Configuration baseconf = new BaseConfiguration();
-        final DataConfiguration conf = new DataConfiguration(baseconf);
-
-        assertEquals("base configuration", baseconf, conf.getConfiguration());
+    public void testClearProperty() {
+        final String key = "test.property";
+        conf.addProperty(key, "someValue");
+        conf.clearProperty(key);
+        assertFalse("Property still found", conf.containsKey(key));
     }
 
+    /**
+     * Tests the implementation of clearPropertyDirect().
+     */
     @Test
-    public void testIsEmpty() {
-        final Configuration baseconf = new BaseConfiguration();
-        final DataConfiguration conf = new DataConfiguration(baseconf);
-
-        assertTrue("not empty", conf.isEmpty());
-
-        baseconf.setProperty("foo", "bar");
+    public void testClearPropertyDirect() {
+        final String key = "test.property";
+        conf.addProperty(key, "someValue");
+        conf.clearPropertyDirect(key);
+        assertFalse("Property still found", conf.containsKey(key));
+    }
 
-        assertFalse("empty", conf.isEmpty());
+    /**
+     * Tests clearPropertyDirect() if the wrapped configuration does not extend AbstractConfiguration.
+     */
+    @Test
+    public void testClearPropertyDirectNoAbstractConf() {
+        final Configuration wrapped = EasyMock.createMock(Configuration.class);
+        final String key = "test.property";
+        wrapped.clearProperty(key);
+        EasyMock.replay(wrapped);
+        conf = new DataConfiguration(wrapped);
+        conf.clearPropertyDirect(key);
+        EasyMock.verify(wrapped);
     }
 
     @Test
@@ -371,1980 +407,1944 @@ public class TestDataConfiguration {
     }
 
     @Test
-    public void testGetKeys() {
-        final Configuration baseconf = new BaseConfiguration();
-        final DataConfiguration conf = new DataConfiguration(baseconf);
+    public void testConversionException() throws Exception {
+        conf.addProperty("key1", new Object());
+        conf.addProperty("key2", "xxxxxx");
 
-        baseconf.setProperty("foo", "bar");
+        try {
+            conf.getBooleanArray("key1");
+            fail("getBooleanArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        final Iterator<String> it = conf.getKeys();
-        assertTrue("the iterator is empty", it.hasNext());
-        assertEquals("unique key", "foo", it.next());
-        assertFalse("the iterator is not exhausted", it.hasNext());
-    }
+        try {
+            conf.getBooleanArray("key2");
+            fail("getBooleanArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test(expected = ConversionException.class)
-    public void testGetInvalidType() {
-        conf.get(Boolean.class, "url.object", null);
-    }
+        try {
+            conf.getBooleanList("key1");
+            fail("getBooleanList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test
-    public void testGetUnknown() {
-        assertNull("non null object for a missing key", conf.get(Object.class, "unknownkey"));
-    }
+        try {
+            conf.getBooleanList("key2");
+            fail("getBooleanList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test(expected = NoSuchElementException.class)
-    public void testGetUnknownException() {
-        conf.setThrowExceptionOnMissing(true);
-        conf.get(Object.class, "unknownkey");
-    }
+        try {
+            conf.getByteArray("key1");
+            fail("getByteArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testGetArrayInvalidDefaultType() {
-        conf.getArray(Boolean.class, "unknownkey", new URL[] {});
-    }
+        try {
+            conf.getByteArray("key2");
+            fail("getByteArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test(expected = ConversionException.class)
-    public void testGetPrimitiveArrayInvalidType() {
-        conf.getArray(Boolean.TYPE, "calendar.list4");
-    }
+        try {
+            conf.getByteList("key1");
+            fail("getByteList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test
-    public void testGetBooleanArray() {
-        // missing list
-        final boolean[] defaultValue = {false, true};
-        ArrayAssert.assertEquals(defaultValue, conf.getBooleanArray("boolean.list", defaultValue));
+        try {
+            conf.getByteList("key2");
+            fail("getByteList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        final boolean[] expected = {true, false};
+        try {
+            conf.getShortArray("key1");
+            fail("getShortArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list1"));
+        try {
+            conf.getShortArray("key2");
+            fail("getShortArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings, comma separated
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list2"));
+        try {
+            conf.getShortList("key1");
+            fail("getShortList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Boolean objects
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list3"));
+        try {
+            conf.getShortList("key2");
+            fail("getShortList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of Boolean objects
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list4"));
+        try {
+            conf.getIntArray("key1");
+            fail("getIntArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of boolean primitives
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list5"));
+        try {
+            conf.getIntArray("key2");
+            fail("getIntArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Boolean objects
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list6"));
+        try {
+            conf.getIntegerList("key1");
+            fail("getIntegerList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of interpolated values
-        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list.interpolated"));
+        try {
+            conf.getIntegerList("key2");
+            fail("getIntegerList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // single boolean values
-        ArrayAssert.assertEquals(new boolean[] {true}, conf.getBooleanArray("boolean.string"));
-        ArrayAssert.assertEquals(new boolean[] {true}, conf.getBooleanArray("boolean.object"));
+        try {
+            conf.getLongArray("key1");
+            fail("getLongArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // empty array
-        ArrayAssert.assertEquals(new boolean[] {}, conf.getBooleanArray("empty"));
-    }
+        try {
+            conf.getLongArray("key2");
+            fail("getLongArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test
-    public void testGetBooleanList() {
-        // missing list
-        ListAssert.assertEquals(null, conf.getBooleanList("boolean.list", null));
+        try {
+            conf.getLongList("key1");
+            fail("getLongList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        List<Object> expected = new ArrayList<>();
-        expected.add(Boolean.TRUE);
-        expected.add(Boolean.FALSE);
+        try {
+            conf.getLongList("key2");
+            fail("getLongList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list1"));
+        try {
+            conf.getFloatArray("key1");
+            fail("getFloatArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings, comma separated
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list2"));
+        try {
+            conf.getFloatArray("key2");
+            fail("getFloatArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Boolean objects
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list3"));
+        try {
+            conf.getFloatList("key1");
+            fail("getFloatList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of Boolean objects
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list4"));
+        try {
+            conf.getFloatList("key2");
+            fail("getFloatList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of boolean primitives
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list5"));
+        try {
+            conf.getDoubleArray("key1");
+            fail("getDoubleArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Boolean objects
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list6"));
+        try {
+            conf.getDoubleArray("key2");
+            fail("getDoubleArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of interpolated values
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list.interpolated"));
+        try {
+            conf.getDoubleList("key1");
+            fail("getDoubleList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // single boolean values
-        expected = new ArrayList<>();
-        expected.add(Boolean.TRUE);
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.string"));
-        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.object"));
+        try {
+            conf.getDoubleList("key2");
+            fail("getDoubleList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // empty list
-        ListAssert.assertEquals(new ArrayList<>(), conf.getBooleanList("empty"));
-    }
+        try {
+            conf.getBigIntegerArray("key1");
+            fail("getBigIntegerArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test
-    public void testGetByteArray() {
-        // missing list
-        final byte[] defaultValue = {1, 2};
-        ArrayAssert.assertEquals(defaultValue, conf.getByteArray("byte.list", defaultValue));
+        try {
+            conf.getBigIntegerArray("key2");
+            fail("getBigIntegerArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        final byte[] expected = {1, 2};
+        try {
+            conf.getBigIntegerList("key1");
+            fail("getBigIntegerList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list1"));
+        try {
+            conf.getBigIntegerList("key2");
+            fail("getBigIntegerList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings, comma separated
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list2"));
+        try {
+            conf.getBigDecimalArray("key1");
+            fail("getBigDecimalArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Byte objects
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list3"));
+        try {
+            conf.getBigDecimalArray("key2");
+            fail("getBigDecimalArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of Byte objects
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list4"));
+        try {
+            conf.getBigDecimalList("key1");
+            fail("getBigDecimalList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of byte primitives
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list5"));
+        try {
+            conf.getBigDecimalList("key2");
+            fail("getBigDecimalList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Byte objects
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list6"));
+        try {
+            conf.getURLArray("key1");
+            fail("getURLArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of interpolated values
-        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list.interpolated"));
+        try {
+            conf.getURLArray("key2");
+            fail("getURLArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // single byte values
-        ArrayAssert.assertEquals(new byte[] {1}, conf.getByteArray("byte.string"));
-        ArrayAssert.assertEquals(new byte[] {1}, conf.getByteArray("byte.object"));
+        try {
+            conf.getURLList("key1");
+            fail("getURLList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // empty array
-        ArrayAssert.assertEquals(new byte[] {}, conf.getByteArray("empty"));
-    }
+        try {
+            conf.getURLList("key2");
+            fail("getURLList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test
-    public void testGetByteList() {
-        // missing list
-        ListAssert.assertEquals(null, conf.getByteList("byte.list", null));
+        try {
+            conf.getLocaleArray("key1");
+            fail("getLocaleArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        List<Object> expected = new ArrayList<>();
-        expected.add(Byte.valueOf("1"));
-        expected.add(Byte.valueOf("2"));
+        try {
+            conf.getLocaleArray("key2");
+            fail("getLocaleArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list1"));
+        try {
+            conf.getLocaleList("key1");
+            fail("getLocaleList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of strings, comma separated
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list2"));
+        try {
+            conf.getLocaleList("key2");
+            fail("getLocaleList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Byte objects
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list3"));
+        try {
+            conf.getColorArray("key1");
+            fail("getColorArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of Byte objects
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list4"));
+        try {
+            conf.getColorArray("key2");
+            fail("getColorArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // array of byte primitives
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list5"));
+        try {
+            conf.getColorList("key1");
+            fail("getColorList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of Byte objects
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list6"));
+        try {
+            conf.getColorList("key2");
+            fail("getColorList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // list of interpolated values
-        ListAssert.assertEquals(expected, conf.getByteList("byte.list.interpolated"));
+        try {
+            conf.getDateArray("key1");
+            fail("getDateArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // single byte values
-        expected = new ArrayList<>();
-        expected.add(Byte.valueOf("1"));
-        ListAssert.assertEquals(expected, conf.getByteList("byte.string"));
-        ListAssert.assertEquals(expected, conf.getByteList("byte.object"));
+        try {
+            conf.getDate("key1", DATE_PATTERN);
+            fail("getDate didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-        // empty list
-        ListAssert.assertEquals(new ArrayList<>(), conf.getByteList("empty"));
-    }
+        try {
+            conf.getDate("key2", DATE_PATTERN);
+            fail("getDate didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
 
-    @Test
-    public void testGetShortArray() {
+        try {
+            conf.getDateArray("key2");
+            fail("getDateArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getDateList("key1");
+            fail("getDateList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getDateList("key2");
+            fail("getDateList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getCalendar("key1", DATE_PATTERN);
+            fail("getCalendar didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getCalendar("key2", DATE_PATTERN);
+            fail("getCalendar didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getCalendarArray("key1");
+            fail("getCalendarArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getCalendarArray("key2");
+            fail("getCalendarArray didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getCalendarList("key1");
+            fail("getCalendarList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.getCalendarList("key2");
+            fail("getCalendarList didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.get(InetAddress.class, "key1");
+            fail("getInetAddress didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+
+        try {
+            conf.get(Class.forName("javax.mail.internet.InternetAddress"), "key1");
+            fail("getInternetAddress didn't throw a ConversionException");
+        } catch (final ConversionException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Tests that the cause of a conversion exception is kept.
+     */
+    @Test
+    public void testConversionExceptionCause() {
+        try {
+            conf.get(Integer.TYPE, "uri.string");
+            fail("No conversion exception thrown!");
+        } catch (final ConversionException cex) {
+            assertTrue("Wrong cause", cex.getCause() instanceof NumberFormatException);
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetArrayInvalidDefaultType() {
+        conf.getArray(Boolean.class, "unknownkey", new URL[] {});
+    }
+
+    @Test
+    public void testGetBigDecimalArray() {
         // missing list
-        final short[] defaultValue = {2, 1};
-        ArrayAssert.assertEquals(defaultValue, conf.getShortArray("short.list", defaultValue));
+        final BigDecimal[] defaultValue = {new BigDecimal("2"), new BigDecimal("1")};
+        ArrayAssert.assertEquals(defaultValue, conf.getBigDecimalArray("bigdecimal.list", defaultValue));
 
-        final short[] expected = {1, 2};
+        final BigDecimal[] expected = {new BigDecimal("1"), new BigDecimal("2")};
 
         // list of strings
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list1"));
+        ArrayAssert.assertEquals(expected, conf.getBigDecimalArray("bigdecimal.list1"));
 
         // list of strings, comma separated
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list2"));
-
-        // list of Byte objects
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list3"));
+        ArrayAssert.assertEquals(expected, conf.getBigDecimalArray("bigdecimal.list2"));
 
-        // array of Byte objects
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list4"));
+        // list of BigDecimal objects
+        ArrayAssert.assertEquals(expected, conf.getBigDecimalArray("bigdecimal.list3"));
 
-        // array of byte primitives
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list5"));
+        // array of BigDecimal objects
+        ArrayAssert.assertEquals(expected, conf.getBigDecimalArray("bigdecimal.list4"));
 
-        // list of Byte objects
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list6"));
+        // list of BigDecimal objects
+        ArrayAssert.assertEquals(expected, conf.getBigDecimalArray("bigdecimal.list6"));
 
         // list of interpolated values
-        ArrayAssert.assertEquals(expected, conf.getShortArray("short.list.interpolated"));
+        ArrayAssert.assertEquals(expected, conf.getBigDecimalArray("bigdecimal.list.interpolated"));
 
-        // single byte values
-        ArrayAssert.assertEquals(new short[] {1}, conf.getShortArray("short.string"));
-        ArrayAssert.assertEquals(new short[] {1}, conf.getShortArray("short.object"));
+        // single BigDecimal values
+        ArrayAssert.assertEquals(new BigDecimal[] {new BigDecimal("1")}, conf.getBigDecimalArray("bigdecimal.string"));
+        ArrayAssert.assertEquals(new BigDecimal[] {new BigDecimal("1")}, conf.getBigDecimalArray("bigdecimal.object"));
 
         // empty array
-        ArrayAssert.assertEquals(new short[] {}, conf.getShortArray("empty"));
+        ArrayAssert.assertEquals(new BigDecimal[] {}, conf.getBigDecimalArray("empty"));
     }
 
     @Test
-    public void testGetShortList() {
+    public void testGetBigDecimalList() {
         // missing list
-        ListAssert.assertEquals(null, conf.getShortList("short.list", null));
+        ListAssert.assertEquals(null, conf.getBigDecimalList("bigdecimal.list", null));
 
         List<Object> expected = new ArrayList<>();
-        expected.add(Short.valueOf("1"));
-        expected.add(Short.valueOf("2"));
+        expected.add(new BigDecimal("1"));
+        expected.add(new BigDecimal("2"));
 
         // list of strings
-        ListAssert.assertEquals(expected, conf.getShortList("short.list1"));
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.list1"));
 
         // list of strings, comma separated
-        ListAssert.assertEquals(expected, conf.getShortList("short.list2"));
-
-        // list of Short objects
-        ListAssert.assertEquals(expected, conf.getShortList("short.list3"));
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.list2"));
 
-        // array of Short objects
-        ListAssert.assertEquals(expected, conf.getShortList("short.list4"));
+        // list of BigDecimal objects
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.list3"));
 
-        // array of short primitives
-        ListAssert.assertEquals(expected, conf.getShortList("short.list5"));
+        // array of BigDecimal objects
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.list4"));
 
-        // list of Short objects
-        ListAssert.assertEquals(expected, conf.getShortList("short.list6"));
+        // list of BigDecimal objects
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.list6"));
 
         // list of interpolated values
-        ListAssert.assertEquals(expected, conf.getShortList("short.list.interpolated"));
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.list.interpolated"));
 
-        // single short values
+        // single BigDecimal values
         expected = new ArrayList<>();
-        expected.add(Short.valueOf("1"));
-        ListAssert.assertEquals(expected, conf.getShortList("short.string"));
-        ListAssert.assertEquals(expected, conf.getShortList("short.object"));
+        expected.add(new BigDecimal("1"));
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.string"));
+        ListAssert.assertEquals(expected, conf.getBigDecimalList("bigdecimal.object"));
 
         // empty list
-        ListAssert.assertEquals(new ArrayList<>(), conf.getShortList("empty"));
+        ListAssert.assertEquals(new ArrayList<>(), conf.getBigDecimalList("empty"));
     }
 
     @Test
-    public void testGetIntegerArray() {
+    public void testGetBigIntegerArray() {
         // missing list
-        final int[] defaultValue = {2, 1};
-        ArrayAssert.assertEquals(defaultValue, conf.getIntArray("integer.list", defaultValue));
+        final BigInteger[] defaultValue = {new BigInteger("2"), new BigInteger("1")};
+        ArrayAssert.assertEquals(defaultValue, conf.getBigIntegerArray("biginteger.list", defaultValue));
 
-        final int[] expected = {1, 2};
+        final BigInteger[] expected = {new BigInteger("1"), new BigInteger("2")};
 
         // list of strings
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list1"));
+        ArrayAssert.assertEquals(expected, conf.getBigIntegerArray("biginteger.list1"));
 
         // list of strings, comma separated
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list2"));
-
-        // list of Integer objects
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list3"));
+        ArrayAssert.assertEquals(expected, conf.getBigIntegerArray("biginteger.list2"));
 
-        // array of Integer objects
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list4"));
+        // list of BigInteger objects
+        ArrayAssert.assertEquals(expected, conf.getBigIntegerArray("biginteger.list3"));
 
-        // array of int primitives
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list5"));
+        // array of BigInteger objects
+        ArrayAssert.assertEquals(expected, conf.getBigIntegerArray("biginteger.list4"));
 
-        // list of Integer objects
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list6"));
+        // list of BigInteger objects
+        ArrayAssert.assertEquals(expected, conf.getBigIntegerArray("biginteger.list6"));
 
         // list of interpolated values
-        ArrayAssert.assertEquals(expected, conf.getIntArray("integer.list.interpolated"));
+        ArrayAssert.assertEquals(expected, conf.getBigIntegerArray("biginteger.list.interpolated"));
 
-        // single int values
-        ArrayAssert.assertEquals(new int[] {1}, conf.getIntArray("integer.string"));
-        ArrayAssert.assertEquals(new int[] {1}, conf.getIntArray("integer.object"));
+        // single BigInteger values
+        ArrayAssert.assertEquals(new BigInteger[] {new BigInteger("1")}, conf.getBigIntegerArray("biginteger.string"));
+        ArrayAssert.assertEquals(new BigInteger[] {new BigInteger("1")}, conf.getBigIntegerArray("biginteger.object"));
 
         // empty array
-        ArrayAssert.assertEquals(new int[] {}, conf.getIntArray("empty"));
+        ArrayAssert.assertEquals(new BigInteger[] {}, conf.getBigIntegerArray("empty"));
     }
 
     @Test
-    public void testGetIntegerList() {
+    public void testGetBigIntegerList() {
         // missing list
-        ListAssert.assertEquals(null, conf.getIntegerList("integer.list", null));
+        final List<BigInteger> bigIntegerList = conf.getBigIntegerList("biginteger.list", null);
+        ListAssert.assertEquals(null, bigIntegerList);
 
         List<Object> expected = new ArrayList<>();
-        expected.add(Integer.valueOf("1"));
-        expected.add(Integer.valueOf("2"));
+        expected.add(new BigInteger("1"));
+        expected.add(new BigInteger("2"));
 
         // list of strings
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list1"));
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.list1"));
 
         // list of strings, comma separated
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list2"));
-
-        // list of Integer objects
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list3"));
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.list2"));
 
-        // array of Integer objects
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list4"));
+        // list of BigInteger objects
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.list3"));
 
-        // array of int primitives
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list5"));
+        // array of BigInteger objects
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.list4"));
 
-        // list of Integer objects
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list6"));
+        // list of BigInteger objects
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.list6"));
 
         // list of interpolated values
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.list.interpolated"));
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.list.interpolated"));
 
-        // single int values
+        // single BigInteger values
         expected = new ArrayList<>();
-        expected.add(Integer.valueOf("1"));
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.string"));
-        ListAssert.assertEquals(expected, conf.getIntegerList("integer.object"));
+        expected.add(new BigInteger("1"));
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.string"));
+        ListAssert.assertEquals(expected, conf.getBigIntegerList("biginteger.object"));
 
         // empty list
-        ListAssert.assertEquals(new ArrayList<>(), conf.getIntegerList("empty"));
+        ListAssert.assertEquals(new ArrayList<>(), conf.getBigIntegerList("empty"));
     }
 
     @Test
-    public void testGetLongArray() {
+    public void testGetBooleanArray() {
         // missing list
-        final long[] defaultValue = {2, 1};
-        ArrayAssert.assertEquals(defaultValue, conf.getLongArray("long.list", defaultValue));
+        final boolean[] defaultValue = {false, true};
+        ArrayAssert.assertEquals(defaultValue, conf.getBooleanArray("boolean.list", defaultValue));
 
-        final long[] expected = {1, 2};
+        final boolean[] expected = {true, false};
 
         // list of strings
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list1"));
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list1"));
 
         // list of strings, comma separated
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list2"));
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list2"));
 
-        // list of Long objects
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list3"));
+        // list of Boolean objects
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list3"));
 
-        // array of Long objects
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list4"));
+        // array of Boolean objects
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list4"));
 
-        // array of long primitives
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list5"));
+        // array of boolean primitives
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list5"));
 
-        // list of Long objects
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list6"));
+        // list of Boolean objects
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list6"));
 
         // list of interpolated values
-        ArrayAssert.assertEquals(expected, conf.getLongArray("long.list.interpolated"));
+        ArrayAssert.assertEquals(expected, conf.getBooleanArray("boolean.list.interpolated"));
 
-        // single long values
-        ArrayAssert.assertEquals(new long[] {1}, conf.getLongArray("long.string"));
-        ArrayAssert.assertEquals(new long[] {1}, conf.getLongArray("long.object"));
+        // single boolean values
+        ArrayAssert.assertEquals(new boolean[] {true}, conf.getBooleanArray("boolean.string"));
+        ArrayAssert.assertEquals(new boolean[] {true}, conf.getBooleanArray("boolean.object"));
 
         // empty array
-        ArrayAssert.assertEquals(new long[] {}, conf.getLongArray("empty"));
+        ArrayAssert.assertEquals(new boolean[] {}, conf.getBooleanArray("empty"));
     }
 
     @Test
-    public void testGetLongList() {
+    public void testGetBooleanList() {
         // missing list
-        ListAssert.assertEquals(null, conf.getLongList("long.list", null));
+        ListAssert.assertEquals(null, conf.getBooleanList("boolean.list", null));
 
         List<Object> expected = new ArrayList<>();
-        expected.add(Long.valueOf("1"));
-        expected.add(Long.valueOf("2"));
+        expected.add(Boolean.TRUE);
+        expected.add(Boolean.FALSE);
 
         // list of strings
-        ListAssert.assertEquals(expected, conf.getLongList("long.list1"));
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list1"));
 
         // list of strings, comma separated
-        ListAssert.assertEquals(expected, conf.getLongList("long.list2"));
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list2"));
 
-        // list of Long objects
-        ListAssert.assertEquals(expected, conf.getLongList("long.list3"));
+        // list of Boolean objects
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list3"));
 
-        // array of Long objects
-        ListAssert.assertEquals(expected, conf.getLongList("long.list4"));
+        // array of Boolean objects
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list4"));
 
-        // array of long primitives
-        ListAssert.assertEquals(expected, conf.getLongList("long.list5"));
+        // array of boolean primitives
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list5"));
 
-        // list of Long objects
-        ListAssert.assertEquals(expected, conf.getLongList("long.list6"));
+        // list of Boolean objects
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list6"));
 
         // list of interpolated values
-        ListAssert.assertEquals(expected, conf.getLongList("long.list.interpolated"));
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.list.interpolated"));
 
-        // single long values
+        // single boolean values
         expected = new ArrayList<>();
-        expected.add(Long.valueOf("1"));
-        ListAssert.assertEquals(expected, conf.getLongList("long.string"));
-        ListAssert.assertEquals(expected, conf.getLongList("long.object"));
+        expected.add(Boolean.TRUE);
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.string"));
+        ListAssert.assertEquals(expected, conf.getBooleanList("boolean.object"));
 
         // empty list
-        ListAssert.assertEquals(new ArrayList<>(), conf.getLongList("empty"));
+        ListAssert.assertEquals(new ArrayList<>(), conf.getBooleanList("empty"));
     }
 
     @Test
-    public void testGetFloatArray() {
+    public void testGetByteArray() {
         // missing list
-        final float[] defaultValue = {2, 1};
-        ArrayAssert.assertEquals(defaultValue, conf.getFloatArray("float.list", defaultValue), 0);
+        final byte[] defaultValue = {1, 2};
+        ArrayAssert.assertEquals(defaultValue, conf.getByteArray("byte.list", defaultValue));
 
-        final float[] expected = {1, 2};
+        final byte[] expected = {1, 2};
 
         // list of strings
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list1"), 0);
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list1"));
 
         // list of strings, comma separated
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list2"), 0);
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list2"));
 
-        // list of Float objects
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list3"), 0);
+        // list of Byte objects
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list3"));
 
-        // array of Float objects
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list4"), 0);
+        // array of Byte objects
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list4"));
 
-        // array of float primitives
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list5"), 0);
+        // array of byte primitives
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list5"));
 
-        // list of Float objects
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list6"), 0);
+        // list of Byte objects
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list6"));
 
         // list of interpolated values
-        ArrayAssert.assertEquals(expected, conf.getFloatArray("float.list.interpolated"), 0);
+        ArrayAssert.assertEquals(expected, conf.getByteArray("byte.list.interpolated"));
 
-        // single float values
-        ArrayAssert.assertEquals(new float[] {1}, conf.getFloatArray("float.string"), 0);
-        ArrayAssert.assertEquals(new float[] {1}, conf.getFloatArray("float.object"), 0);
+        // single byte values
+        ArrayAssert.assertEquals(new byte[] {1}, conf.getByteArray("byte.string"));
+        ArrayAssert.assertEquals(new byte[] {1}, conf.getByteArray("byte.object"));
 
         // empty array
-        ArrayAssert.assertEquals(new float[] {}, conf.getFloatArray("empty"), 0);
+        ArrayAssert.assertEquals(new byte[] {}, conf.getByteArray("empty"));
     }
 
     @Test
-    public void testGetFloatList() {
+    public void testGetByteList() {
         // missing list
-        ListAssert.assertEquals(null, conf.getFloatList("float.list", null));
+        ListAssert.assertEquals(null, conf.getByteList("byte.list", null));
 
         List<Object> expected = new ArrayList<>();
-        expected.add(Float.valueOf("1"));
-        expected.add(Float.valueOf("2"));
+        expected.add(Byte.valueOf("1"));
+        expected.add(Byte.valueOf("2"));
 
         // list of strings
-        ListAssert.assertEquals(expected, conf.getFloatList("float.list1"));
+        ListAssert.assertEquals(expected, conf.getByteList("byte.list1"));
 
         // list of strings, comma separated
-        ListAssert.assertEquals(expected, conf.getFloatList("float.list2"));
+        ListAssert.assertEquals(expected, conf.getByteList("byte.list2"));
 
-        // list of Float objects
-        ListAssert.assertEquals(expected, conf.getFloatList("float.list3"));
+        // list of Byte objects
+        ListAssert.assertEquals(expected, conf.getByteList("byte.list3"));
 
-        // array of Float objects
... 38422 lines suppressed ...