You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2013/01/13 22:16:41 UTC

svn commit: r1432744 - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/builder/combined/ test/java/org/apache/commons/configuration/builder/combined/

Author: oheger
Date: Sun Jan 13 21:16:40 2013
New Revision: 1432744

URL: http://svn.apache.org/viewvc?rev=1432744&view=rev
Log:
Added MultiFileConfigurationBuilder (as a replacement for MultiFileHierarchicalConfiguration).

Added:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java   (with props)
    commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java   (with props)

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java?rev=1432744&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java (added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java Sun Jan 13 21:16:40 2013
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.builder.combined;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.FileBasedConfiguration;
+import org.apache.commons.configuration.builder.BasicBuilderParameters;
+import org.apache.commons.configuration.builder.BasicConfigurationBuilder;
+import org.apache.commons.configuration.builder.BuilderListener;
+import org.apache.commons.configuration.builder.BuilderParameters;
+import org.apache.commons.configuration.builder.ConfigurationBuilder;
+import org.apache.commons.configuration.builder.FileBasedBuilderParametersImpl;
+import org.apache.commons.configuration.builder.FileBasedConfigurationBuilder;
+import org.apache.commons.configuration.event.ConfigurationErrorListener;
+import org.apache.commons.configuration.event.ConfigurationListener;
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.lang3.concurrent.ConcurrentUtils;
+
+/**
+ * <p>
+ * A specialized {@code ConfigurationBuilder} implementation providing access to
+ * multiple file-based configurations based on a file name pattern.
+ * </p>
+ * <p>
+ * This builder class is initialized with a pattern string and a
+ * {@link ConfigurationInterpolator} object. Each time a configuration is
+ * requested, the pattern is evaluated against the
+ * {@code ConfigurationInterpolator} (so all variables are replaced by their
+ * current values). The resulting string is interpreted as a file name for a
+ * configuration file to be loaded. For example, providing a pattern of
+ * <em>file:///opt/config/${product}/${client}/config.xml</em> will result in
+ * <em>product</em> and <em>client</em> being resolved on every call. By storing
+ * configuration files in a corresponding directory structure, specialized
+ * configuration files associated with a specific product and client can be
+ * loaded. Thus an application can be made multi-tenant in a transparent way.
+ * </p>
+ * <p>
+ * This builder class keeps a map with configuration builders for configurations
+ * already loaded. The {@code getConfiguration()} method first evaluates the
+ * pattern string and checks whether a builder for the resulting file name is
+ * available. If yes, it is queried for its configuration. Otherwise, a new
+ * file-based configuration builder is created now and initialized.
+ * </p>
+ * <p>
+ * Configuration of an instance happens in the usual way for configuration
+ * builders. A {@link MultiFileBuilderParametersImpl} parameters object is
+ * expected which must contain a file name pattern string and a
+ * {@code ConfigurationInterpolator}. Other properties of this parameters object
+ * are used to initialize the builders for managed configurations.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ * @param <T> the concrete type of {@code Configuration} objects created by this
+ *        builder
+ */
+public class MultiFileConfigurationBuilder<T extends FileBasedConfiguration>
+        extends BasicConfigurationBuilder<T>
+{
+    /**
+     * Constant for the name of the key referencing the
+     * {@code ConfigurationInterpolator} in this builder's parameters.
+     */
+    private static final String KEY_INTERPOLATOR = "interpolator";
+
+    /** A cache for already created managed builders. */
+    private final ConcurrentMap<String, FileBasedConfigurationBuilder<T>> managedBuilders =
+            new ConcurrentHashMap<String, FileBasedConfigurationBuilder<T>>();
+
+    /**
+     * A specialized builder listener which gets registered at all managed
+     * builders. This listener just propagates notifications from managed
+     * builders to the listeners registered at this
+     * {@code MultiFileConfigurationBuilder}.
+     */
+    private final BuilderListener managedBuilderDelegationListener =
+            new BuilderListener()
+            {
+                public void builderReset(
+                        ConfigurationBuilder<? extends Configuration> builder)
+                {
+                    resetResult();
+                }
+            };
+
+    /**
+     * Creates a new instance of {@code MultiFileConfigurationBuilder} and sets
+     * initialization parameters and a flag whether initialization failures
+     * should be ignored.
+     *
+     * @param resCls the result configuration class
+     * @param params a map with initialization parameters
+     * @param allowFailOnInit a flag whether initialization errors should be
+     *        ignored
+     * @throws IllegalArgumentException if the result class is <b>null</b>
+     */
+    public MultiFileConfigurationBuilder(Class<T> resCls,
+            Map<String, Object> params, boolean allowFailOnInit)
+    {
+        super(resCls, params, allowFailOnInit);
+    }
+
+    /**
+     * Creates a new instance of {@code MultiFileConfigurationBuilder} and sets
+     * initialization parameters.
+     *
+     * @param resCls the result configuration class
+     * @param params a map with initialization parameters
+     * @throws IllegalArgumentException if the result class is <b>null</b>
+     */
+    public MultiFileConfigurationBuilder(Class<T> resCls,
+            Map<String, Object> params)
+    {
+        super(resCls, params);
+    }
+
+    /**
+     * Creates a new instance of {@code MultiFileConfigurationBuilder} without
+     * setting initialization parameters.
+     *
+     * @param resCls the result configuration class
+     * @throws IllegalArgumentException if the result class is <b>null</b>
+     */
+    public MultiFileConfigurationBuilder(Class<T> resCls)
+    {
+        super(resCls);
+    }
+
+    /**
+     * {@inheritDoc} This implementation evaluates the file name pattern using
+     * the configured {@code ConfigurationInterpolator}. If this file has
+     * already been loaded, the corresponding builder is accessed. Otherwise, a
+     * new builder is created for loading this configuration file.
+     */
+    @Override
+    public T getConfiguration() throws ConfigurationException
+    {
+        Map<String, Object> params = getParameters();
+        MultiFileBuilderParametersImpl multiParams =
+                MultiFileBuilderParametersImpl.fromParameters(params, true);
+        if (multiParams.getFilePattern() == null)
+        {
+            throw new ConfigurationException("No file name pattern is set!");
+        }
+        String fileName = constructFileName(params, multiParams);
+
+        FileBasedConfigurationBuilder<T> builder =
+                managedBuilders.get(fileName);
+        if (builder == null)
+        {
+            builder =
+                    createInitializedManagedBuilder(fileName,
+                            createManagedBuilderParameters(params, multiParams));
+            FileBasedConfigurationBuilder<T> newBuilder =
+                    ConcurrentUtils.putIfAbsent(managedBuilders, fileName,
+                            builder);
+            if (newBuilder == builder)
+            {
+                initListeners(newBuilder);
+            }
+            else
+            {
+                builder = newBuilder;
+            }
+        }
+
+        return builder.getConfiguration();
+    }
+
+    /**
+     * {@inheritDoc} This implementation ensures that the listener is also added
+     * at managed configuration builders.
+     */
+    @Override
+    public synchronized BasicConfigurationBuilder<T> addConfigurationListener(
+            ConfigurationListener l)
+    {
+        super.addConfigurationListener(l);
+        for (FileBasedConfigurationBuilder<T> b : managedBuilders.values())
+        {
+            b.addConfigurationListener(l);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc} This implementation ensures that the listener is also
+     * removed from managed configuration builders.
+     */
+    @Override
+    public synchronized BasicConfigurationBuilder<T> removeConfigurationListener(
+            ConfigurationListener l)
+    {
+        super.removeConfigurationListener(l);
+        for (FileBasedConfigurationBuilder<T> b : managedBuilders.values())
+        {
+            b.removeConfigurationListener(l);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc} This implementation ensures that the listener is also added
+     * at managed configuration builders.
+     */
+    @Override
+    public synchronized BasicConfigurationBuilder<T> addErrorListener(
+            ConfigurationErrorListener l)
+    {
+        super.addErrorListener(l);
+        for (FileBasedConfigurationBuilder<T> b : managedBuilders.values())
+        {
+            b.addErrorListener(l);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc} This implementation ensures that the listener is also
+     * removed from managed configuration builders.
+     */
+    @Override
+    public synchronized BasicConfigurationBuilder<T> removeErrorListener(
+            ConfigurationErrorListener l)
+    {
+        super.removeErrorListener(l);
+        for (FileBasedConfigurationBuilder<T> b : managedBuilders.values())
+        {
+            b.removeErrorListener(l);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc} This implementation clears the cache with all managed
+     * builders.
+     */
+    @Override
+    public synchronized void resetParameters()
+    {
+        for (FileBasedConfigurationBuilder<T> b : managedBuilders.values())
+        {
+            b.removeBuilderListener(managedBuilderDelegationListener);
+        }
+        managedBuilders.clear();
+        super.resetParameters();
+    }
+
+    /**
+     * Determines the file name of a configuration based on the file name
+     * pattern. This method is called on every access to this builder's
+     * configuration. It obtains the {@link ConfigurationInterpolator} from this
+     * builder's parameters and uses it to interpolate the file name pattern.
+     *
+     * @param paramsMap the map with the current parameters of this builder
+     * @param multiParams the parameters object for this builder
+     * @return the name of the configuration file to be loaded
+     */
+    protected String constructFileName(Map<String, Object> paramsMap,
+            MultiFileBuilderParametersImpl multiParams)
+    {
+        ConfigurationInterpolator ci =
+                BasicBuilderParameters.fetchInterpolator(paramsMap);
+        // TODO Make this more generic, handle missing CI
+        return String.valueOf(ci.interpolate(multiParams.getFilePattern()));
+    }
+
+    /**
+     * Creates a builder for a managed configuration. This method is called
+     * whenever a configuration for a file name is requested which has not yet
+     * been loaded. The passed in map with parameters is populated from this
+     * builder's configuration (i.e. the basic parameters plus the optional
+     * parameters for managed builders). This base implementation creates a
+     * standard builder for file-based configurations. Derived classes may
+     * override it to create special purpose builders.
+     *
+     * @param fileName the name of the file to be loaded
+     * @param params a map with initialization parameters for the new builder
+     * @return the newly created builder instance
+     * @throws ConfigurationException if an error occurs
+     */
+    protected FileBasedConfigurationBuilder<T> createManagedBuilder(
+            String fileName, Map<String, Object> params)
+            throws ConfigurationException
+    {
+        return new FileBasedConfigurationBuilder<T>(getResultClass(), params,
+                isAllowFailOnInit());
+    }
+
+    /**
+     * Creates a fully initialized builder for a managed configuration. This
+     * method is called by {@code getConfiguration()} whenever a configuration
+     * file is requested which has not yet been loaded. This implementation
+     * delegates to {@code createManagedBuilder()} for actually creating the
+     * builder object. Then it sets the location to the configuration file.
+     *
+     * @param fileName the name of the file to be loaded
+     * @param params a map with initialization parameters for the new builder
+     * @return the newly created and initialized builder instance
+     * @throws ConfigurationException if an error occurs
+     */
+    protected FileBasedConfigurationBuilder<T> createInitializedManagedBuilder(
+            String fileName, Map<String, Object> params)
+            throws ConfigurationException
+    {
+        FileBasedConfigurationBuilder<T> managedBuilder =
+                createManagedBuilder(fileName, params);
+        managedBuilder.getFileHandler().setFileName(fileName);
+        return managedBuilder;
+    }
+
+    /**
+     * Registers event listeners at the passed in newly created managed builder.
+     * This method registers a special {@code BuilderListener} which propagates
+     * builder events to listeners registered at this builder. In addition,
+     * {@code ConfigurationListener} and {@code ConfigurationErrorListener}
+     * objects are registered at the new builder.
+     *
+     * @param newBuilder the builder to be initialized
+     */
+    private void initListeners(FileBasedConfigurationBuilder<T> newBuilder)
+    {
+        copyEventListeners(newBuilder);
+        newBuilder.addBuilderListener(managedBuilderDelegationListener);
+    }
+
+    /**
+     * Creates a map with parameters for a new managed configuration builder.
+     * This method merges the basic parameters set for this builder with the
+     * specific parameters object for managed builders (if provided).
+     *
+     * @param params the parameters of this builder
+     * @param multiParams the parameters object for this builder
+     * @return the parameters for a new managed builder
+     */
+    private static Map<String, Object> createManagedBuilderParameters(
+            Map<String, Object> params,
+            MultiFileBuilderParametersImpl multiParams)
+    {
+        Map<String, Object> newParams = new HashMap<String, Object>(params);
+        newParams.remove(KEY_INTERPOLATOR);
+        BuilderParameters managedBuilderParameters =
+                multiParams.getManagedBuilderParameters();
+        if (managedBuilderParameters != null)
+        {
+            newParams.putAll(managedBuilderParameters.getParameters());
+        }
+
+        // ensure that file-based parameters are available
+        if (FileBasedBuilderParametersImpl.fromParameters(newParams) == null)
+        {
+            newParams.putAll(new FileBasedBuilderParametersImpl()
+                    .getParameters());
+        }
+        return newParams;
+    }
+}

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java?rev=1432744&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java (added)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java Sun Jan 13 21:16:40 2013
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.builder.combined;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.builder.BasicBuilderParameters;
+import org.apache.commons.configuration.builder.BuilderListener;
+import org.apache.commons.configuration.builder.BuilderParameters;
+import org.apache.commons.configuration.builder.FileBasedConfigurationBuilder;
+import org.apache.commons.configuration.builder.XMLBuilderParametersImpl;
+import org.apache.commons.configuration.event.ConfigurationErrorListener;
+import org.apache.commons.configuration.event.ConfigurationListener;
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.DefaultLookups;
+import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Test;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Test class for {@code MultiFileConfigurationBuilder}.
+ *
+ * @version $Id$
+ */
+public class TestMultiFileConfigurationBuilder
+{
+    /** The system property which selects a sub configuration. */
+    private static final String PROP = "Id";
+
+    /** The pattern for file names. */
+    private static String PATTERN =
+            "target/test-classes/testMultiConfiguration_${sys:Id}.xml";
+
+    @After
+    public void tearDown() throws Exception
+    {
+        System.getProperties().remove(PROP);
+    }
+
+    /**
+     * Sets a system property for accessing a specific configuration file from
+     * the test builder.
+     *
+     * @param id the ID of the managed configuration to load
+     */
+    private static void switchToConfig(String id)
+    {
+        System.setProperty(PROP, id);
+    }
+
+    /**
+     * Selects a specific configuration to be obtained from the builder.
+     *
+     * @param index the index of the configuration to be accessed (valid indices
+     *        are 1-3)
+     */
+    private static void switchToConfig(int index)
+    {
+        switchToConfig("100" + index);
+    }
+
+    /**
+     * Creates a {@code ConfigurationInterpolator} to be used by tests. This
+     * object contains a lookup for system properties.
+     *
+     * @return the new {@code ConfigurationInterpolator}
+     */
+    private static ConfigurationInterpolator createInterpolator()
+    {
+        ConfigurationInterpolator ci = new ConfigurationInterpolator();
+        ci.registerLookup(DefaultLookups.SYSTEM_PROPERTIES.getPrefix(),
+                DefaultLookups.SYSTEM_PROPERTIES.getLookup());
+        return ci;
+    }
+
+    /**
+     * Creates a parameters object with default settings for a test builder
+     * instance.
+     *
+     * @param managedParams the parameters for managed configurations
+     * @return the test parameters
+     */
+    private static BasicBuilderParameters createTestBuilderParameters(
+            BuilderParameters managedParams)
+    {
+        return new MultiFileBuilderParametersImpl().setFilePattern(PATTERN)
+                .setManagedBuilderParameters(managedParams)
+                .setInterpolator(createInterpolator());
+    }
+
+    /**
+     * Creates a test builder object with default settings.
+     *
+     * @param managedParams the parameters for managed configurations
+     * @return the test instance
+     */
+    private static MultiFileConfigurationBuilder<XMLConfiguration> createTestBuilder(
+            BuilderParameters managedParams)
+    {
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                new MultiFileConfigurationBuilder<XMLConfiguration>(
+                        XMLConfiguration.class);
+        builder.configure(createTestBuilderParameters(managedParams));
+        return builder;
+    }
+
+    /**
+     * Creates a test builder instance which allows access to the managed
+     * builders created by it. The returned builder instance overrides the
+     * method for creating managed builders. It stores newly created builders in
+     * the passed in collection.
+     *
+     * @param managedBuilders a collection in which to store managed builders
+     * @return the test builder instance
+     */
+    private static MultiFileConfigurationBuilder<XMLConfiguration> createBuilderWithAccessToManagedBuilders(
+            final Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders)
+    {
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                new MultiFileConfigurationBuilder<XMLConfiguration>(
+                        XMLConfiguration.class)
+                {
+                    @Override
+                    protected FileBasedConfigurationBuilder<XMLConfiguration> createInitializedManagedBuilder(
+                            String fileName,
+                            java.util.Map<String, Object> params)
+                            throws ConfigurationException
+                    {
+                        FileBasedConfigurationBuilder<XMLConfiguration> result =
+                                super.createInitializedManagedBuilder(fileName,
+                                        params);
+                        managedBuilders.add(result);
+                        return result;
+                    }
+                };
+        builder.configure(createTestBuilderParameters(null));
+        return builder;
+    }
+
+    /**
+     * Tests whether access to multiple configurations works.
+     */
+    @Test
+    public void testGetConfiguration() throws ConfigurationException
+    {
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createTestBuilder(null);
+        String key = "rowsPerPage";
+        switchToConfig(1);
+        assertEquals("Wrong property (1)", 15, builder.getConfiguration()
+                .getInt(key));
+        switchToConfig(2);
+        assertEquals("Wrong property (2)", 25, builder.getConfiguration()
+                .getInt(key));
+        switchToConfig(3);
+        assertEquals("Wrong property (3)", 35, builder.getConfiguration()
+                .getInt(key));
+    }
+
+    /**
+     * Tests whether a managed configuration is properly initialized.
+     */
+    @Test
+    public void testManagedConfigurationSettings()
+            throws ConfigurationException
+    {
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                new MultiFileConfigurationBuilder<XMLConfiguration>(
+                        XMLConfiguration.class);
+        ExpressionEngine engine = new XPathExpressionEngine();
+        BuilderParameters xmlParams =
+                new XMLBuilderParametersImpl().setExpressionEngine(engine)
+                        .setListDelimiter(';');
+        MultiFileBuilderParametersImpl params =
+                new MultiFileBuilderParametersImpl().setFilePattern(PATTERN)
+                        .setManagedBuilderParameters(xmlParams);
+        ConfigurationInterpolator ci = createInterpolator();
+        params.setInterpolator(ci).setListDelimiter('#');
+        builder.configure(params);
+        switchToConfig(1);
+        XMLConfiguration config = builder.getConfiguration();
+        assertSame("Wrong expression engine", engine,
+                config.getExpressionEngine());
+        assertEquals("Wrong list delimiter", ';', config.getListDelimiter());
+        assertNotSame("Interpolator was copied", ci, config.getInterpolator());
+    }
+
+    /**
+     * Tests whether XML schema validation can be enabled.
+     */
+    @Test
+    public void testSchemaValidationError() throws ConfigurationException
+    {
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createTestBuilder(new XMLBuilderParametersImpl().setValidating(
+                        true).setSchemaValidation(true));
+        switchToConfig("2001");
+        try
+        {
+            builder.getConfiguration();
+            fail("No exception thrown");
+        }
+        catch (ConfigurationException ex)
+        {
+            Throwable cause = ex.getCause();
+            while (cause != null && !(cause instanceof SAXParseException))
+            {
+                cause = cause.getCause();
+            }
+            assertTrue("SAXParseException was not thrown",
+                    cause instanceof SAXParseException);
+        }
+    }
+
+    /**
+     * Tests the behavior if a configuration is accessed which cannot be
+     * located.
+     */
+    @Test(expected = ConfigurationException.class)
+    public void testFileNotFound() throws ConfigurationException
+    {
+        switchToConfig("unknown configuration ID");
+        createTestBuilder(null).getConfiguration();
+    }
+
+    /**
+     * Tests whether exceptions when creating configurations can be suppressed.
+     */
+    @Test
+    public void testFileNotFoundAllowFailOnInit() throws ConfigurationException
+    {
+        BasicBuilderParameters params = createTestBuilderParameters(null);
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                new MultiFileConfigurationBuilder<XMLConfiguration>(
+                        XMLConfiguration.class, params.getParameters(), true);
+        switchToConfig("unknown configuration ID");
+        XMLConfiguration config = builder.getConfiguration();
+        assertTrue("Got content", config.isEmpty());
+    }
+
+    /**
+     * Tests whether a missing file name pattern causes an exception.
+     */
+    @Test(expected = ConfigurationException.class)
+    public void testNoPattern() throws ConfigurationException
+    {
+        BasicBuilderParameters params =
+                new MultiFileBuilderParametersImpl()
+                        .setInterpolator(createInterpolator());
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                new MultiFileConfigurationBuilder<XMLConfiguration>(
+                        XMLConfiguration.class, params.getParameters(), true);
+        switchToConfig(1);
+        builder.getConfiguration();
+    }
+
+    /**
+     * Tests whether configuration listeners are handled correctly.
+     */
+    @Test
+    public void testAddConfigurationListener() throws ConfigurationException
+    {
+        ConfigurationListener l1 =
+                EasyMock.createMock(ConfigurationListener.class);
+        ConfigurationListener l2 =
+                EasyMock.createMock(ConfigurationListener.class);
+        EasyMock.replay(l1, l2);
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createTestBuilder(null);
+        assertSame("Wrong result", builder,
+                builder.addConfigurationListener(l1));
+        switchToConfig(1);
+        XMLConfiguration config = builder.getConfiguration();
+        assertTrue("Listener not added", config.getConfigurationListeners()
+                .contains(l1));
+        builder.addConfigurationListener(l2);
+        assertTrue("Listener 2 not added", config.getConfigurationListeners()
+                .contains(l2));
+        builder.removeConfigurationListener(l2);
+        assertFalse("Listener not removed", config.getConfigurationListeners()
+                .contains(l2));
+        switchToConfig(2);
+        XMLConfiguration config2 = builder.getConfiguration();
+        assertFalse("Listener not globally removed", config2
+                .getConfigurationListeners().contains(l2));
+    }
+
+    /**
+     * Tests whether error listeners are handled correctly.
+     */
+    @Test
+    public void testAddErrorListener() throws ConfigurationException
+    {
+        ConfigurationErrorListener l1 =
+                EasyMock.createMock(ConfigurationErrorListener.class);
+        ConfigurationErrorListener l2 =
+                EasyMock.createMock(ConfigurationErrorListener.class);
+        EasyMock.replay(l1, l2);
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createTestBuilder(null);
+        assertSame("Wrong result", builder, builder.addErrorListener(l1));
+        switchToConfig(1);
+        XMLConfiguration config = builder.getConfiguration();
+        assertTrue("Listener not added", config.getErrorListeners()
+                .contains(l1));
+        builder.addErrorListener(l2);
+        assertTrue("Listener 2 not added",
+                config.getErrorListeners().contains(l2));
+        builder.removeErrorListener(l2);
+        assertFalse("Listener not removed", config.getErrorListeners()
+                .contains(l2));
+        switchToConfig(2);
+        XMLConfiguration config2 = builder.getConfiguration();
+        assertFalse("Listener not globally removed", config2
+                .getErrorListeners().contains(l2));
+    }
+
+    /**
+     * Tests whether managed builders are cached.
+     */
+    @Test
+    public void testCaching() throws ConfigurationException
+    {
+        Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders =
+                new ArrayList<FileBasedConfigurationBuilder<XMLConfiguration>>();
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createBuilderWithAccessToManagedBuilders(managedBuilders);
+        switchToConfig(1);
+        builder.getConfiguration();
+        assertEquals("Wrong number of managed builders (1)", 1,
+                managedBuilders.size());
+        builder.getConfiguration();
+        assertEquals("Wrong number of managed builders (2)", 1,
+                managedBuilders.size());
+        switchToConfig(2);
+        builder.getConfiguration();
+        assertEquals("Wrong number of managed builders (3)", 2,
+                managedBuilders.size());
+    }
+
+    /**
+     * Tests whether a reset of the builder configuration also flushes the
+     * cache.
+     */
+    @Test
+    public void testCachingWithReset() throws ConfigurationException
+    {
+        Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders =
+                new ArrayList<FileBasedConfigurationBuilder<XMLConfiguration>>();
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createBuilderWithAccessToManagedBuilders(managedBuilders);
+        switchToConfig(1);
+        builder.getConfiguration();
+        builder.resetParameters();
+        builder.configure(createTestBuilderParameters(null));
+        builder.getConfiguration();
+        assertEquals("Wrong number of managed builders", 2,
+                managedBuilders.size());
+    }
+
+    /**
+     * Tests whether builder listeners are handled correctly.
+     */
+    @Test
+    public void testBuilderListener() throws ConfigurationException
+    {
+        BuilderListener listener = EasyMock.createMock(BuilderListener.class);
+        Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders =
+                new ArrayList<FileBasedConfigurationBuilder<XMLConfiguration>>();
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createBuilderWithAccessToManagedBuilders(managedBuilders);
+        listener.builderReset(builder);
+        EasyMock.replay(listener);
+        switchToConfig(1);
+        builder.addBuilderListener(listener);
+        builder.getConfiguration();
+        managedBuilders.iterator().next().resetResult();
+        EasyMock.verify(listener);
+    }
+
+    /**
+     * Tests whether listeners at managed builders are removed when the cache is
+     * cleared.
+     */
+    @Test
+    public void testRemoveBuilderListenerOnReset()
+            throws ConfigurationException
+    {
+        BuilderListener listener = EasyMock.createMock(BuilderListener.class);
+        Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders =
+                new ArrayList<FileBasedConfigurationBuilder<XMLConfiguration>>();
+        MultiFileConfigurationBuilder<XMLConfiguration> builder =
+                createBuilderWithAccessToManagedBuilders(managedBuilders);
+        EasyMock.replay(listener);
+        switchToConfig(1);
+        builder.addBuilderListener(listener);
+        builder.getConfiguration();
+        builder.resetParameters();
+        managedBuilders.iterator().next().resetResult();
+        EasyMock.verify(listener);
+    }
+}

Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain