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 2009/10/05 22:32:07 UTC

svn commit: r821996 - in /commons/proper/configuration/branches/configuration2_experimental/src: main/java/org/apache/commons/configuration2/base/ test/java/org/apache/commons/configuration2/base/

Author: oheger
Date: Mon Oct  5 20:32:07 2009
New Revision: 821996

URL: http://svn.apache.org/viewvc?rev=821996&view=rev
Log:
Added a new abstract base class for ConfigurationSource implementations.

Added:
    commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java   (with props)
    commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java   (with props)

Added: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java?rev=821996&view=auto
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java (added)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java Mon Oct  5 20:32:07 2009
@@ -0,0 +1,164 @@
+/*
+ * 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.configuration2.base;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * <p>
+ * An abstract base class for implementations of the {@code ConfigurationSource}
+ * interface.
+ * </p>
+ * <p>
+ * This class can be used as super class to simplify the implementation of a
+ * concrete {@code ConfigurationSource}. It already provides basic
+ * implementations of some of the methods defined by the {@code
+ * ConfigurationSource} interface. Especially the handling of
+ * <em>capabilities</em> is fully implemented. Derived classes can hook into
+ * this mechanism by defining custom capabilities in the
+ * {@link #appendCapabilities(Collection)} method. Optional methods, e.g. the
+ * methods for registering event listeners, are implemented by throwing an
+ * {@code UnsupportedOperationException}.
+ * </p>
+ *
+ * @author Commons Configuration team
+ * @version $Id$
+ */
+public abstract class AbstractConfigurationSource implements
+        ConfigurationSource
+{
+    /** Stores the capabilities associated with this source. */
+    private final AtomicReference<Capabilities> capabilities;
+
+    /**
+     * Creates a new instance of {@code AbstractConfigurationSource}.
+     */
+    protected AbstractConfigurationSource()
+    {
+        capabilities = new AtomicReference<Capabilities>();
+    }
+
+    /**
+     * Adds a {@code ConfigurationSourceListener} to this object. This is just a
+     * dummy implementation that throws a {@code UnsupportedOperationException}.
+     *
+     * @param l the listener to be added
+     */
+    public void addConfigurationSourceListener(ConfigurationSourceListener l)
+    {
+        throw new UnsupportedOperationException("Not implemented!");
+    }
+
+    /**
+     * Returns the capability of the the specified type. This implementation
+     * delegates to a {@link Capabilities} object maintained internally which is
+     * created on first access by {@link #createCapabilities()}. It takes care
+     * for proper synchronization.
+     *
+     * @param <T> the type of the capability requested
+     * @param cls the class of the capability interface
+     * @return the object implementing the desired capability or <b>null</b> if
+     *         this capability is not supported
+     */
+    public <T> T getCapability(Class<T> cls)
+    {
+        return getCapabilities().getCapability(cls);
+    }
+
+    /**
+     * Removes the specified {@code ConfigurationSourceListener} from this
+     * object. This is just a dummy implementation that throws a {@code
+     * UnsupportedOperationException}.
+     *
+     * @param l the listener to be removed
+     */
+    public boolean removeConfigurationSourceListener(
+            ConfigurationSourceListener l)
+    {
+        throw new UnsupportedOperationException("Not implemented!");
+    }
+
+    /**
+     * Returns the {@code Capabilities} object that manages the capabilities
+     * supported by this {@code ConfigurationSource}. The object is created on
+     * first access. <em>Implementation note:</em> For synchronizing access to
+     * the {@code Capabilities} object an atomic variable is used. If no object
+     * has been created yet, {@link #createCapabilities()} is called. If this
+     * method is called by multiple threads, it is possible that
+     * {@link #createCapabilities()} is invoked multiple times. However, it is
+     * ensured that this method always returns the same {@code Capabilities}
+     * instance.
+     *
+     * @return the {@code Capabilities} object associated with this {@code
+     *         ConfigurationSource}
+     */
+    protected Capabilities getCapabilities()
+    {
+        Capabilities caps = capabilities.get();
+
+        if (caps == null)
+        {
+            Capabilities capsNew = createCapabilities();
+            if (capabilities.compareAndSet(null, capsNew))
+            {
+                caps = capsNew;
+            }
+            else
+            {
+                caps = capabilities.get();
+            }
+        }
+
+        return caps;
+    }
+
+    /**
+     * Creates a {@code Capabilities} object for managing the capabilities
+     * supported by this {@code ConfigurationSource}. This method is called by
+     * {@link #getCapability(Class)} on first access. This implementation calls
+     * {@link #appendCapabilities(Collection)} to obtain a list of additional
+     * capabilities. Then it creates a {@code Capabilities} instance with this
+     * list and this {@code ConfigurationSource} object as owner.
+     *
+     * @return the {@code Capabilities} supported by this {@code
+     *         ConfigurationSource}
+     */
+    protected Capabilities createCapabilities()
+    {
+        Collection<Capability> caps = new LinkedList<Capability>();
+        appendCapabilities(caps);
+        return new Capabilities(this, caps);
+    }
+
+    /**
+     * Creates additional {@code Capability} objects and adds them to the passed
+     * in list. This method can be overridden by derived classes to add {@code
+     * Capability} instances to the internal capability management for
+     * interfaces that are not implemented by this object. This base
+     * implementation is empty. Classes overriding this method should always
+     * call the super method to ensure that capabilities of the base classes do
+     * not get lost.
+     *
+     * @param caps a collection for adding additional {@code Capability}
+     *        instances
+     */
+    protected void appendCapabilities(Collection<Capability> caps)
+    {
+    }
+}

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/AbstractConfigurationSource.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java?rev=821996&view=auto
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java (added)
+++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java Mon Oct  5 20:32:07 2009
@@ -0,0 +1,201 @@
+/*
+ * 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.configuration2.base;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+
+/**
+ * Test class for {@code AbstractConfigurationSource}.
+ *
+ * @author Commons Configuration team
+ * @version $Id$
+ */
+public class TestAbstractConfigurationSource extends TestCase
+{
+    /** The source to be tested. */
+    private AbstractConfigurationSourceTestImpl source;
+
+    @Override
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        source = new AbstractConfigurationSourceTestImpl();
+    }
+
+    /**
+     * Tries to add a configuration listener. This is not supported.
+     */
+    public void testAddConfigurationListener()
+    {
+        ConfigurationSourceListener l = EasyMock
+                .createMock(ConfigurationSourceListener.class);
+        EasyMock.replay(l);
+        try
+        {
+            source.addConfigurationSourceListener(l);
+            fail("Could add a listener!");
+        }
+        catch (UnsupportedOperationException uoex)
+        {
+            EasyMock.verify(l);
+        }
+    }
+
+    /**
+     * Tries to remove a configuration listener. This is not supported.
+     */
+    public void testRemoveConfigurationListener()
+    {
+        ConfigurationSourceListener l = EasyMock
+                .createMock(ConfigurationSourceListener.class);
+        EasyMock.replay(l);
+        try
+        {
+            source.removeConfigurationSourceListener(l);
+            fail("Could remove a listener!");
+        }
+        catch (UnsupportedOperationException uoex)
+        {
+            EasyMock.verify(l);
+        }
+    }
+
+    /**
+     * Tests whether capabilities from implemented interfaces can be queried.
+     */
+    public void testGetCapabilityImplemented()
+    {
+        assertEquals("Wrong source capability", source, source
+                .getCapability(ConfigurationSource.class));
+        assertEquals("Wrong runnable capability", source, source
+                .getCapability(Runnable.class));
+    }
+
+    /**
+     * Tests whether capabilities provided by appendCapabilities() can be
+     * queried.
+     */
+    public void testGetCapabilityProvided()
+    {
+        Runnable r = EasyMock.createNiceMock(Runnable.class);
+        source.capabilityList = Collections.singleton(new Capability(
+                Runnable.class, r));
+        assertEquals("Wrong runnable capability", r, source
+                .getCapability(Runnable.class));
+    }
+
+    /**
+     * Tests whether the capabilities object is created once and cached.
+     */
+    public void testGetCapabilitiesCached()
+    {
+        Capabilities caps = source.getCapabilities();
+        assertNotNull("No capabilities", caps);
+        assertSame("Different instance", caps, source.getCapabilities());
+    }
+
+    /**
+     * Tests concurrent access to the capabilities.
+     */
+    public void testGetCapabilitiesMultiThreaded() throws InterruptedException
+    {
+        final int threadCount = 64;
+        CountDownLatch latch = new CountDownLatch(1);
+        CapabilitiesTestThread[] threads = new CapabilitiesTestThread[threadCount];
+        for (int i = 0; i < threadCount; i++)
+        {
+            threads[i] = new CapabilitiesTestThread(latch);
+            threads[i].start();
+        }
+        latch.countDown(); // start all threads
+        Capabilities caps = source.getCapabilities();
+        for (CapabilitiesTestThread t : threads)
+        {
+            t.join();
+            assertSame("Different capabilities", caps, t.caps);
+        }
+    }
+
+    /**
+     * A concrete test implementation of {@code AbstractConfigurationSource}.
+     */
+    private static class AbstractConfigurationSourceTestImpl extends
+            AbstractConfigurationSource implements Runnable
+    {
+        /** A list with capabilities to be added to the capabilities object. */
+        Collection<Capability> capabilityList;
+
+        public void clear()
+        {
+        }
+
+        public void run()
+        {
+        }
+
+        /**
+         * Appends capabilities if the list is defined.
+         */
+        @Override
+        protected void appendCapabilities(Collection<Capability> caps)
+        {
+            super.appendCapabilities(caps);
+            if (capabilityList != null)
+            {
+                caps.addAll(capabilityList);
+            }
+        }
+    }
+
+    /**
+     * A test thread class for testing concurrent access to the source's
+     * capabilities.
+     */
+    private class CapabilitiesTestThread extends Thread
+    {
+        /** The latch for synchronizing the start. */
+        private final CountDownLatch startLatch;
+
+        /** The capabilities obtained from the source. */
+        Capabilities caps;
+
+        public CapabilitiesTestThread(CountDownLatch latch)
+        {
+            startLatch = latch;
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                startLatch.await();
+                caps = source.getCapabilities();
+            }
+            catch (InterruptedException iex)
+            {
+                // fall through
+            }
+        }
+    }
+}

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestAbstractConfigurationSource.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain