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 2012/12/26 16:52:27 UTC

svn commit: r1425960 - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/AbstractConfiguration.java test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java

Author: oheger
Date: Wed Dec 26 15:52:27 2012
New Revision: 1425960

URL: http://svn.apache.org/viewvc?rev=1425960&view=rev
Log:
[CONFIGURATION-518] Added set methods to AbstractConfiguration to manipulate the Lookup objects of the current ConfigurationInterpolator.

Modified:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
    commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java?rev=1425960&r1=1425959&r2=1425960&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java Wed Dec 26 15:52:27 2012
@@ -30,6 +30,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.commons.configuration.event.BaseEventSource;
 import org.apache.commons.configuration.event.ConfigurationErrorEvent;
@@ -145,7 +146,7 @@ public abstract class AbstractConfigurat
     private boolean throwExceptionOnMissing;
 
     /** Stores a reference to the object that handles variable interpolation. */
-    private volatile ConfigurationInterpolator interpolator;
+    private final AtomicReference<ConfigurationInterpolator> interpolator;
 
     /** Stores the logger.*/
     private Log log;
@@ -155,6 +156,7 @@ public abstract class AbstractConfigurat
      */
     public AbstractConfiguration()
     {
+        interpolator = new AtomicReference<ConfigurationInterpolator>();
         setLogger(null);
         installDefaultInterpolator();
     }
@@ -294,7 +296,7 @@ public abstract class AbstractConfigurat
      */
     public ConfigurationInterpolator getInterpolator()
     {
-        return interpolator;
+        return interpolator.get();
     }
 
     /**
@@ -306,7 +308,7 @@ public abstract class AbstractConfigurat
      */
     public final void setInterpolator(ConfigurationInterpolator ci)
     {
-        interpolator = ci;
+        interpolator.set(ci);
     }
 
     /**
@@ -329,6 +331,74 @@ public abstract class AbstractConfigurat
     }
 
     /**
+     * Registers all {@code Lookup} objects in the given map at the current
+     * {@code ConfigurationInterpolator} of this configuration. The set of
+     * default lookup objects (for variables without a prefix) is not modified
+     * by this method. If this configuration does not have a
+     * {@code ConfigurationInterpolator}, a new instance is created. Note: This
+     * method is mainly intended to be used for initializing a configuration
+     * when it is created by a builder. Normal client code should better call
+     * {@link #installInterpolator(Map, Collection)} to define the
+     * {@code ConfigurationInterpolator} in a single step.
+     *
+     * @param lookups a map with new {@code Lookup} objects and their prefixes
+     *        (may be <b>null</b>)
+     */
+    public final void setPrefixLookups(Map<String, ? extends Lookup> lookups)
+    {
+        boolean success;
+        do
+        {
+            // do this in a loop because the ConfigurationInterpolator
+            // instance may be changed by another thread
+            ConfigurationInterpolator ciOld = getInterpolator();
+            ConfigurationInterpolator ciNew =
+                    (ciOld != null) ? ciOld : new ConfigurationInterpolator();
+            ciNew.registerLookups(lookups);
+            success = interpolator.compareAndSet(ciOld, ciNew);
+        } while (!success);
+    }
+
+    /**
+     * Adds all {@code Lookup} objects in the given collection as default
+     * lookups (i.e. lookups without a variable prefix) to the
+     * {@code ConfigurationInterpolator} object of this configuration. In
+     * addition, it adds a specialized default {@code Lookup} object which
+     * queries this {@code Configuration}. The set of {@code Lookup} objects
+     * with prefixes is not modified by this method. If this configuration does
+     * not have a {@code ConfigurationInterpolator}, a new instance is created.
+     * Note: This method is mainly intended to be used for initializing a
+     * configuration when it is created by a builder. Normal client code should
+     * better call {@link #installInterpolator(Map, Collection)} to define the
+     * {@code ConfigurationInterpolator} in a single step.
+     *
+     * @param lookups the collection with default {@code Lookup} objects to be
+     *        added
+     */
+    public final void setDefaultLookups(Collection<? extends Lookup> lookups)
+    {
+        boolean success;
+        do
+        {
+            ConfigurationInterpolator ciOld = getInterpolator();
+            ConfigurationInterpolator ciNew =
+                    (ciOld != null) ? ciOld : new ConfigurationInterpolator();
+            Lookup confLookup = findConfigurationLookup(ciNew);
+            if (confLookup == null)
+            {
+                confLookup = new ConfigurationLookup(this);
+            }
+            else
+            {
+                ciNew.removeDefaultLookup(confLookup);
+            }
+            ciNew.addDefaultLookups(lookups);
+            ciNew.addDefaultLookup(confLookup);
+            success = interpolator.compareAndSet(ciOld, ciNew);
+        } while (!success);
+    }
+
+    /**
      * Creates a default {@code ConfigurationInterpolator} which is initialized
      * with all default {@code Lookup} objects. This method is called by the
      * constructor. It ensures that default interpolation works for every new
@@ -345,6 +415,30 @@ public abstract class AbstractConfigurat
     }
 
     /**
+     * Finds a {@code ConfigurationLookup} pointing to this configuration in the
+     * default lookups of the specified {@code ConfigurationInterpolator}. This
+     * method is called to ensure that there is exactly one default lookup
+     * querying this configuration.
+     *
+     * @param ci the {@code ConfigurationInterpolator} in question
+     * @return the found {@code Lookup} object or <b>null</b>
+     */
+    private Lookup findConfigurationLookup(ConfigurationInterpolator ci)
+    {
+        for (Lookup l : ci.getDefaultLookups())
+        {
+            if (l instanceof ConfigurationLookup)
+            {
+                if (this == ((ConfigurationLookup) l).getConfiguration())
+                {
+                    return l;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the logger used by this configuration object.
      *
      * @return the logger

Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java?rev=1425960&r1=1425959&r2=1425960&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java Wed Dec 26 15:52:27 2012
@@ -17,11 +17,13 @@
 package org.apache.commons.configuration;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -30,6 +32,8 @@ import java.util.Map;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.configuration.event.ConfigurationEvent;
 import org.apache.commons.configuration.event.ConfigurationListener;
+import org.apache.commons.configuration.interpol.Lookup;
+import org.easymock.EasyMock;
 import org.junit.Test;
 
 /**
@@ -281,6 +285,103 @@ public class TestAbstractConfigurationBa
     }
 
     /**
+     * Tests whether prefix lookups can be added to an existing
+     * {@code ConfigurationInterpolator}.
+     */
+    @Test
+    public void testSetPrefixLookupsExistingInterpolator()
+    {
+        Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        AbstractConfiguration config =
+                new TestConfigurationImpl(new PropertiesConfiguration());
+        int count = config.getInterpolator().getLookups().size();
+        Map<String, Lookup> lookups = new HashMap<String, Lookup>();
+        lookups.put("test", look);
+        config.setPrefixLookups(lookups);
+        Map<String, Lookup> lookups2 = config.getInterpolator().getLookups();
+        assertEquals("Not added", count + 1, lookups2.size());
+        assertSame("Not found", look, lookups2.get("test"));
+    }
+
+    /**
+     * Tests whether prefix lookups can be added if no
+     * {@code ConfigurationInterpolator} exists yet.
+     */
+    @Test
+    public void testSetPrefixLookupsNoInterpolator()
+    {
+        Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        AbstractConfiguration config =
+                new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setInterpolator(null);
+        config.setPrefixLookups(Collections.singletonMap("test", look));
+        Map<String, Lookup> lookups = config.getInterpolator().getLookups();
+        assertEquals("Wrong number of lookups", 1, lookups.size());
+        assertSame("Not found", look, lookups.get("test"));
+    }
+
+    /**
+     * Tests whether default lookups can be added to an already existing
+     * {@code ConfigurationInterpolator}.
+     */
+    @Test
+    public void testSetDefaultLookupsExistingInterpolator()
+    {
+        Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        AbstractConfiguration config =
+                new TestConfigurationImpl(new PropertiesConfiguration());
+        config.getInterpolator().addDefaultLookup(
+                new ConfigurationLookup(new PropertiesConfiguration()));
+        config.setDefaultLookups(Collections.singleton(look));
+        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 whether default lookups can be added if not
+     * {@code ConfigurationInterpolator} exists yet.
+     */
+    @Test
+    public void testSetDefaultLookupsNoInterpolator()
+    {
+        Lookup look = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(look);
+        AbstractConfiguration config =
+                new TestConfigurationImpl(new PropertiesConfiguration());
+        config.setInterpolator(null);
+        config.setDefaultLookups(Collections.singleton(look));
+        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);
+    }
+
+    /**
+     * Tests whether a new {@code ConfigurationInterpolator} can be installed
+     * without providing custom lookups.
+     */
+    @Test
+    public void testInstallInterpolatorNull()
+    {
+        AbstractConfiguration config =
+                new TestConfigurationImpl(new PropertiesConfiguration());
+        config.installInterpolator(null, null);
+        assertTrue("Got prefix lookups", config.getInterpolator().getLookups()
+                .isEmpty());
+        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 getList() for single non-string values.
      */
     @Test