commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r1393698 - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/builder/BasicConfigurationBuilder.java test/java/org/apache/commons/configuration/builder/TestBasicConfigurationBuilder.java
Date Wed, 03 Oct 2012 19:18:11 GMT
Author: oheger
Date: Wed Oct  3 19:18:11 2012
New Revision: 1393698

URL: http://svn.apache.org/viewvc?rev=1393698&view=rev
Log:
Initial implementation of BasicConfigurationBuilder.

Added:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicConfigurationBuilder.java
  (with props)
    commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicConfigurationBuilder.java
  (with props)

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicConfigurationBuilder.java?rev=1393698&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicConfigurationBuilder.java
(added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicConfigurationBuilder.java
Wed Oct  3 19:18:11 2012
@@ -0,0 +1,416 @@
+/*
+ * 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;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.ConfigurationRuntimeException;
+import org.apache.commons.configuration.beanutils.BeanDeclaration;
+import org.apache.commons.configuration.beanutils.BeanHelper;
+
+/**
+ * <p>
+ * An implementation of the {@code ConfigurationBuilder} interface which is able
+ * to create different concrete {@code Configuration} implementations based on
+ * reflection.
+ * </p>
+ * <p>
+ * When constructing an instance of this class the concrete
+ * {@code Configuration} implementation class has to be provided. Then
+ * properties for the new {@code Configuration} instance can be set. The first
+ * call to {@code getConfiguration()} creates and initializes the new
+ * {@code Configuration} object. It is cached and returned by subsequent calls.
+ * This cache - and also the initialization properties set so far - can be
+ * flushed by calling one of the {@code reset()} methods. That way other
+ * {@code Configuration} instances with different properties can be created.
+ * </p>
+ * <p>
+ * There are multiple options for setting up a {@code BasicConfigurationBuilder}
+ * instance:
+ * <ul>
+ * <li>All initialization properties can be set in one or multiple calls of the
+ * {@code configure()} method. In each call an arbitrary number of
+ * {@link BuilderParameters} objects can be passed. The API allows method
+ * chaining and is intended to be used from Java code.</li>
+ * <li>If builder instances are created by other means - e.g. using a dependency
+ * injection framework -, the fluent API approach may not be suitable. For those
+ * use cases it is also possible to pass in all initialization parameters as a
+ * map. The keys of the map have to match initialization properties of the
+ * {@code Configuration} object to be created, the values are the corresponding
+ * property values. For instance, the key <em>throwExceptionOnMissing</em> in
+ * the map will cause the method {@code setThrowExceptionOnMissing()} on the
+ * {@code Configuration} object to be called with the corresponding value as
+ * parameter.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This class is thread-safe. Multiple threads can modify initialization
+ * properties and call {@code getConfiguration()}. However, the intended use
+ * case is that the builder is configured by a single thread first. Then
+ * {@code getConfiguration()} can be called concurrently, and it is guaranteed
+ * that always the same {@code Configuration} instance is returned until the
+ * builder is reset.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ * @param <T> the concrete type of {@code Configuration} objects created by this
+ *        builder
+ */
+public class BasicConfigurationBuilder<T extends Configuration> implements
+        ConfigurationBuilder<T>
+{
+    /** The class of the objects produced by this builder instance. */
+    private final Class<T> resultClass;
+
+    /** The map with current initialization parameters. */
+    private Map<String, Object> parameters;
+
+    /** The current bean declaration. */
+    private BeanDeclaration resultDeclaration;
+
+    /** The result object of this builder. */
+    private volatile T result;
+
+    /**
+     * Creates a new instance of {@code BasicConfigurationBuilder} and
+     * initializes it with the given result class. No initialization properties
+     * are set.
+     *
+     * @param resCls the result class (must not be <b>null</b>
+     * @throws IllegalArgumentException if the result class is <b>null</b>
+     */
+    public BasicConfigurationBuilder(Class<T> resCls)
+    {
+        this(resCls, null);
+    }
+
+    /**
+     * Creates a new instance of {@code BasicConfigurationBuilder} and
+     * initializes it with the given result class and an initial set of builder
+     * parameters. The map with parameters may be <b>null</b>, in this case no
+     * initialization parameters are set.
+     *
+     * @param resCls the result class (must not be <b>null</b>
+     * @param params a map with initialization parameters
+     * @throws IllegalArgumentException if the result class is <b>null</b>
+     */
+    public BasicConfigurationBuilder(Class<T> resCls, Map<String, Object> params)
+    {
+        if (resCls == null)
+        {
+            throw new IllegalArgumentException("Result class must not be null!");
+        }
+
+        resultClass = resCls;
+        updateParameters(params);
+    }
+
+    /**
+     * Returns the result class of this builder. The objects produced by this
+     * builder have the class returned here.
+     *
+     * @return the result class of this builder
+     */
+    public Class<T> getResultClass()
+    {
+        return resultClass;
+    }
+
+    /**
+     * Sets the initialization parameters of this builder. Already existing
+     * parameters are replaced by the content of the given map.
+     *
+     * @param params the new initialization parameters of this builder; can be
+     *        <b>null</b>, then all initialization parameters are removed
+     * @return a reference to this builder for method chaining
+     */
+    public synchronized BasicConfigurationBuilder<T> setParameters(
+            Map<String, Object> params)
+    {
+        updateParameters(params);
+        return this;
+    }
+
+    /**
+     * Adds the content of the given map to the already existing initialization
+     * parameters.
+     *
+     * @param params the map with additional initialization parameters; may be
+     *        <b>null</b>, then this call has no effect
+     * @return a reference to this builder for method chaining
+     */
+    public synchronized BasicConfigurationBuilder<T> addParameters(
+            Map<String, Object> params)
+    {
+        Map<String, Object> newParams =
+                new HashMap<String, Object>(getParameters());
+        if (params != null)
+        {
+            newParams.putAll(params);
+        }
+        updateParameters(newParams);
+        return this;
+    }
+
+    /**
+     * Appends the content of the specified {@code BuilderParameters} objects to
+     * the current initialization parameters. Calling this method multiple times
+     * will create a union of the parameters provided.
+     *
+     * @param params an arbitrary number of objects with builder parameters
+     * @return a reference to this builder for method chaining
+     * @throws NullPointerException if a <b>null</b> array is passed
+     */
+    public BasicConfigurationBuilder<T> configure(BuilderParameters... params)
+    {
+        Map<String, Object> newParams = new HashMap<String, Object>();
+        for (BuilderParameters p : params)
+        {
+            newParams.putAll(p.getParameters());
+        }
+
+        return setParameters(newParams);
+    }
+
+    /**
+     * {@inheritDoc} This implementation creates the result configuration on
+     * first access. Later invocations return the same object until this builder
+     * is reset. The double-check idiom for lazy initialization is used (Bloch,
+     * Effective Java, item 71).
+     */
+    public T getConfiguration() throws ConfigurationException
+    {
+        T resObj = result;
+        if (resObj == null)
+        {
+            synchronized (this)
+            {
+                resObj = result;
+                if (resObj == null)
+                {
+                    result = resObj = createResult();
+                }
+            }
+        }
+        return resObj;
+    }
+
+    /**
+     * Clears an existing result object. An invocation of this method causes a
+     * new {@code Configuration} object to be created the next time
+     * {@link #getConfiguration()} is called.
+     */
+    public synchronized void resetResult()
+    {
+        result = null;
+        resultDeclaration = null;
+    }
+
+    /**
+     * Removes all initialization parameters of this builder. This method can be
+     * called if this builder is to be reused for creating result objects with a
+     * different configuration.
+     */
+    public void resetParameters()
+    {
+        setParameters(null);
+    }
+
+    /**
+     * Resets this builder. This is a convenience method which combines calls to
+     * {@link #resetResult()} and {@link #resetParameters()}.
+     */
+    public synchronized void reset()
+    {
+        resetParameters();
+        resetResult();
+    }
+
+    /**
+     * Creates a new, initialized result object. This method is called by
+     * {@code getConfiguration()} if no valid result object exists. This base
+     * implementation performs two steps:
+     * <ul>
+     * <li>{@code createResultInstance()} is called to create a new,
+     * uninitialized result object.</li>
+     * <li>{@code initResultInstance()} is called to process all initialization
+     * parameters.</li>
+     * </ul>
+     * Note that this method is called in a synchronized block.
+     *
+     * @return the newly created result object
+     * @throws ConfigurationException if an error occurs
+     */
+    protected T createResult() throws ConfigurationException
+    {
+        T resObj = createResultInstance();
+        initResultInstance(resObj);
+        return resObj;
+    }
+
+    /**
+     * Creates the new, uninitialized result object. This is the first step of
+     * the process of producing a result object for this builder. This
+     * implementation uses the {@link BeanHelper} class to create a new object
+     * based on the {@link BeanDeclaration} returned by
+     * {@link #getResultDeclaration()}. Note: This method is invoked in a
+     * synchronized block.
+     *
+     * @return the newly created, yet uninitialized result object
+     * @throws ConfigurationException if an exception occurs
+     */
+    protected T createResultInstance() throws ConfigurationException
+    {
+        return getResultClass().cast(
+                BeanHelper.createBean(getResultDeclaration()));
+    }
+
+    /**
+     * Initializes a newly created result object. This is the second step of the
+     * process of producing a result object for this builder. This
+     * implementation uses the {@link BeanHelper} class to initialize the
+     * object's property based on the {@link BeanDeclaration} returned by
+     * {@link #getResultDeclaration()}. Note: This method is invoked in a
+     * synchronized block
+     *
+     * @param obj the object to be initialized
+     * @throws ConfigurationException if an error occurs
+     */
+    protected void initResultInstance(T obj) throws ConfigurationException
+    {
+        BeanHelper.initBean(obj, getResultDeclaration());
+    }
+
+    /**
+     * Returns the {@code BeanDeclaration} that is used to create and initialize
+     * result objects. The declaration is created on first access (by invoking
+     * {@link #createResultDeclaration(Map)}) based on the current
+     * initialization parameters.
+     *
+     * @return the {@code BeanDeclaration} for dynamically creating a result
+     *         object
+     */
+    protected synchronized final BeanDeclaration getResultDeclaration()
+    {
+        if (resultDeclaration == null)
+        {
+            resultDeclaration = createResultDeclaration(getParameters());
+            checkResultClass(resultDeclaration);
+        }
+        return resultDeclaration;
+    }
+
+    /**
+     * Returns a (unmodifiable) map with the current initialization parameters
+     * set for this builder. The map is populated with the parameters set using
+     * the various configuration options.
+     *
+     * @return a map with the current set of initialization parameters
+     */
+    protected synchronized final Map<String, Object> getParameters()
+    {
+        if (parameters != null)
+        {
+            return parameters;
+        }
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Creates a new {@code BeanDeclaration} which is used for creating new
+     * result objects dynamically. This implementation creates a specialized
+     * {@code BeanDeclaration} object that is initialized from the given map of
+     * initialization parameters. The {@code BeanDeclaration} must be
+     * initialized with the result class of this builder, otherwise exceptions
+     * will be thrown when the result object is created. Note: This method is
+     * invoked in a synchronized block.
+     *
+     * @param params a snapshot of the current initialization parameters
+     * @return the {@code BeanDeclaration} for creating result objects
+     */
+    protected BeanDeclaration createResultDeclaration(
+            final Map<String, Object> params)
+    {
+        return new BeanDeclaration()
+        {
+            public Map<String, Object> getNestedBeanDeclarations()
+            {
+                // no nested beans
+                return Collections.emptyMap();
+            }
+
+            public Map<String, Object> getBeanProperties()
+            {
+                // the properties are equivalent to the parameters
+                return params;
+            }
+
+            public Object getBeanFactoryParameter()
+            {
+                return null;
+            }
+
+            public String getBeanFactoryName()
+            {
+                return null;
+            }
+
+            public String getBeanClassName()
+            {
+                return getResultClass().getName();
+            }
+        };
+    }
+
+    /**
+     * Replaces the current map with parameters by a new one.
+     *
+     * @param newParams the map with new parameters (may be <b>null</b>)
+     */
+    private void updateParameters(Map<String, Object> newParams)
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        if (newParams != null)
+        {
+            map.putAll(newParams);
+        }
+        parameters = Collections.unmodifiableMap(map);
+    }
+
+    /**
+     * Checks whether the bean class of the given {@code BeanDeclaration} equals
+     * this builder's result class. This is done to ensure that only objects of
+     * the expected result class are created.
+     *
+     * @param decl the declaration to be checked
+     * @throws ConfigurationRuntimeException if an invalid bean class is
+     *         detected
+     */
+    private void checkResultClass(BeanDeclaration decl)
+    {
+        if (!getResultClass().getName().equals(decl.getBeanClassName()))
+        {
+            throw new ConfigurationRuntimeException("Unexpected bean class: "
+                    + decl.getBeanClassName());
+        }
+    }
+}

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

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

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

Added: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicConfigurationBuilder.java?rev=1393698&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicConfigurationBuilder.java
(added)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicConfigurationBuilder.java
Wed Oct  3 19:18:11 2012
@@ -0,0 +1,365 @@
+/*
+ * 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;
+
+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 java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.ConfigurationRuntimeException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.beanutils.BeanDeclaration;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+/**
+ * Test class for {@code BasicConfigurationBuilder}.
+ *
+ * @version $Id$
+ */
+public class TestBasicConfigurationBuilder
+{
+    /**
+     * Tries to create an instance without a result class.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInitNoClass()
+    {
+        Class<Configuration> cls = null;
+        new BasicConfigurationBuilder<Configuration>(cls);
+    }
+
+    /**
+     * Creates a map with test initialization parameters.
+     *
+     * @return the map with parameters
+     */
+    private static Map<String, Object> createTestParameters()
+    {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("throwExceptionOnMissing", Boolean.TRUE);
+        params.put("delimiterParsingDisabled", Boolean.TRUE);
+        params.put("listDelimiter", Character.valueOf('.'));
+        return params;
+    }
+
+    /**
+     * Tests whether initialization parameters can be passed to the constructor.
+     */
+    @Test
+    public void testInitWithParameters()
+    {
+        Map<String, Object> params = createTestParameters();
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, params);
+        Map<String, Object> params2 =
+                new HashMap<String, Object>(builder.getParameters());
+        assertEquals("Wrong parameters", createTestParameters(), params2);
+    }
+
+    /**
+     * Tests whether a copy of the passed in parameters is created.
+     */
+    @Test
+    public void testInitWithParametersDefensiveCopy()
+    {
+        Map<String, Object> params = createTestParameters();
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, params);
+        params.put("anotherParameter", "value");
+        Map<String, Object> params2 =
+                new HashMap<String, Object>(builder.getParameters());
+        assertEquals("Wrong parameters", createTestParameters(), params2);
+    }
+
+    /**
+     * Tests whether null parameters are handled correctly.
+     */
+    @Test
+    public void testInitWithParametersNull()
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, null);
+        assertTrue("Got parameters", builder.getParameters().isEmpty());
+    }
+
+    /**
+     * Tests that the map with parameters cannot be modified.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetParametersModify()
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters());
+        builder.getParameters().clear();
+    }
+
+    /**
+     * Tests whether parameters can be set using the configure() method.
+     */
+    @Test
+    public void testConfigure()
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class)
+                        .configure(new BasicBuilderParameters()
+                                .setDelimiterParsingDisabled(true)
+                                .setListDelimiter('.')
+                                .setThrowExceptionOnMissing(true));
+        Map<String, Object> params2 =
+                new HashMap<String, Object>(builder.getParameters());
+        assertEquals("Wrong parameters", createTestParameters(), params2);
+    }
+
+    /**
+     * Tests whether new parameters can be set to replace existing ones.
+     */
+    @Test
+    public void testSetParameters()
+    {
+        Map<String, Object> params1 = new HashMap<String, Object>();
+        params1.put("someParameter", "value");
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, params1);
+        assertSame("Wrong result", builder,
+                builder.setParameters(createTestParameters()));
+        Map<String, Object> params2 =
+                new HashMap<String, Object>(builder.getParameters());
+        assertEquals("Wrong parameters", createTestParameters(), params2);
+    }
+
+    /**
+     * Tests whether additional parameters can be added.
+     */
+    @Test
+    public void testAddParameters()
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters());
+        Map<String, Object> params = createTestParameters();
+        params.put("anotherParameter", "value");
+        assertSame("Wrong result", builder, builder.addParameters(params));
+        Map<String, Object> params2 = builder.getParameters();
+        assertTrue("No original parameters",
+                params2.keySet().containsAll(createTestParameters().keySet()));
+        assertEquals("Additional parameter not found", "value",
+                params2.get("anotherParameter"));
+    }
+
+    /**
+     * Tests whether null parameters are handled correctly by addParameters().
+     */
+    @Test
+    public void testAddParametersNull()
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters());
+        Map<String, Object> params = builder.getParameters();
+        builder.addParameters(null);
+        assertEquals("Parameters changed", params, builder.getParameters());
+    }
+
+    /**
+     * Tests whether all parameters can be reset.
+     */
+    @Test
+    public void testResetParameters()
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters());
+        builder.resetParameters();
+        assertTrue("Still got parameters", builder.getParameters().isEmpty());
+    }
+
+    /**
+     * Tests whether the builder can create a correctly initialized
+     * configuration object.
+     */
+    @Test
+    public void testGetConfiguration() throws ConfigurationException
+    {
+        PropertiesConfiguration config =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class).configure(
+                        new BasicBuilderParameters().setListDelimiter('*')
+                                .setThrowExceptionOnMissing(true))
+                        .getConfiguration();
+        assertTrue("Delimiter parsing not disabled",
+                config.isDelimiterParsingDisabled());
+        assertTrue("Wrong exception flag", config.isThrowExceptionOnMissing());
+        assertEquals("Wrong list delimiter", '*', config.getListDelimiter());
+    }
+
+    /**
+     * Tests whether the builder can be accessed by multiple threads and that
+     * only a single result object is produced.
+     */
+    @Test
+    public void testGetConfigurationConcurrently() throws Exception
+    {
+        final int threadCount = 32;
+        CountDownLatch startLatch = new CountDownLatch(1);
+        CountDownLatch endLatch = new CountDownLatch(threadCount);
+        ConfigurationBuilder<?> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class);
+        AccessBuilderThread[] threads = new AccessBuilderThread[threadCount];
+        for (int i = 0; i < threadCount; i++)
+        {
+            threads[i] = new AccessBuilderThread(startLatch, endLatch, builder);
+            threads[i].start();
+        }
+        startLatch.countDown();
+        assertTrue("Timeout", endLatch.await(5, TimeUnit.SECONDS));
+        Set<Object> results = new HashSet<Object>();
+        for (AccessBuilderThread t : threads)
+        {
+            results.add(t.result);
+        }
+        assertEquals("Wrong number of result objects", 1, results.size());
+    }
+
+    /**
+     * Tests whether a reset of the result object can be performed.
+     */
+    @Test
+    public void testResetResult() throws ConfigurationException
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters());
+        PropertiesConfiguration config = builder.getConfiguration();
+        builder.resetResult();
+        PropertiesConfiguration config2 = builder.getConfiguration();
+        assertNotSame("No new result", config, config2);
+        assertTrue("Wrong property", config2.isThrowExceptionOnMissing());
+    }
+
+    /**
+     * Tests a full reset of the builder.
+     */
+    @Test
+    public void testReset() throws ConfigurationException
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters());
+        PropertiesConfiguration config = builder.getConfiguration();
+        builder.reset();
+        PropertiesConfiguration config2 = builder.getConfiguration();
+        assertNotSame("No new result", config, config2);
+        assertFalse("Parameters not reset", config2.isThrowExceptionOnMissing());
+    }
+
+    /**
+     * Tests whether a check for the correct bean class is made.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testGetResultDeclarationInvalidBeanClass()
+            throws ConfigurationException
+    {
+        BasicConfigurationBuilder<PropertiesConfiguration> builder =
+                new BasicConfigurationBuilder<PropertiesConfiguration>(
+                        PropertiesConfiguration.class, createTestParameters())
+                {
+                    @Override
+                    protected BeanDeclaration createResultDeclaration(
+                            Map<String, Object> params)
+                    {
+                        BeanDeclaration decl =
+                                EasyMock.createMock(BeanDeclaration.class);
+                        EasyMock.expect(decl.getBeanClassName())
+                                .andReturn(Object.class.getName()).anyTimes();
+                        EasyMock.replay(decl);
+                        return decl;
+                    }
+                };
+        builder.getConfiguration();
+    }
+
+    /**
+     * A test thread class for testing whether the builder's result object can
+     * be requested concurrently.
+     */
+    private static class AccessBuilderThread extends Thread
+    {
+        /** A latch for controlling the start of the thread. */
+        private final CountDownLatch startLatch;
+
+        /** A latch for controlling the end of the thread. */
+        private final CountDownLatch endLatch;
+
+        /** The builder to be accessed. */
+        private final ConfigurationBuilder<?> builder;
+
+        /** The result object obtained from the builder. */
+        private volatile Object result;
+
+        /**
+         * Creates a new instance of {@code AccessBuilderThread}.
+         *
+         * @param lstart the latch for controlling the thread start
+         * @param lend the latch for controlling the thread end
+         * @param bldr the builder to be tested
+         */
+        public AccessBuilderThread(CountDownLatch lstart, CountDownLatch lend,
+                ConfigurationBuilder<?> bldr)
+        {
+            startLatch = lstart;
+            endLatch = lend;
+            builder = bldr;
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                startLatch.await();
+                result = builder.getConfiguration();
+            }
+            catch (Exception ex)
+            {
+                result = ex;
+            }
+            finally
+            {
+                endLatch.countDown();
+            }
+        }
+    }
+}

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

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

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



Mime
View raw message