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/25 22:24:43 UTC
svn commit: r1438718 - in /commons/proper/configuration/trunk/src:
main/java/org/apache/commons/configuration/builder/
main/java/org/apache/commons/configuration/builder/combined/
test/java/org/apache/commons/configuration/builder/ test/java/org/apache...
Author: oheger
Date: Fri Jan 25 21:24:42 2013
New Revision: 1438718
URL: http://svn.apache.org/viewvc?rev=1438718&view=rev
Log:
BasicBuilderParameters now provides a method for obtaining an InterpolatorSpecification object; the method for fetching a ConfigurationInterpolator was dropped.
MultiFileConfigurationBuilder uses this method for obtaining its ConfigurationInterpolator.
Modified:
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicBuilderParameters.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicBuilderParameters.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicBuilderParameters.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicBuilderParameters.java?rev=1438718&r1=1438717&r2=1438718&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicBuilderParameters.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/BasicBuilderParameters.java Fri Jan 25 21:24:42 2013
@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.InterpolatorSpecification;
import org.apache.commons.configuration.interpol.Lookup;
import org.apache.commons.logging.Log;
@@ -255,19 +256,30 @@ public class BasicBuilderParameters impl
}
/**
- * Obtains the {@code ConfigurationInterpolator} from the given map with
- * parameters. If such an object is stored in the map under the correct key,
- * it is returned. Otherwise, result is <b>null</b>.
+ * Obtains a specification for a {@link ConfigurationInterpolator} from the
+ * specified map with parameters. All properties related to interpolation
+ * are evaluated and added to the specification object.
*
* @param params the map with parameters (must not be <b>null</b>)
- * @return the {@code ConfigurationInterpolator} obtained from this map or
- * <b>null</b>
- * @throws NullPointerException if the map is <b>null</b>
+ * @return an {@code InterpolatorSpecification} object constructed with data
+ * from the map
+ * @throws IllegalArgumentException if the map is <b>null</b> or contains
+ * invalid data
*/
- public static ConfigurationInterpolator fetchInterpolator(
+ public static InterpolatorSpecification fetchInterpolatorSpecification(
Map<String, Object> params)
{
- return (ConfigurationInterpolator) params.get(PROP_INTERPOLATOR);
+ checkParameters(params);
+ return new InterpolatorSpecification.Builder()
+ .withInterpolator(
+ fetchParameter(params, PROP_INTERPOLATOR,
+ ConfigurationInterpolator.class))
+ .withParentInterpolator(
+ fetchParameter(params, PROP_PARENT_INTERPOLATOR,
+ ConfigurationInterpolator.class))
+ .withPrefixLookups(fetchAndCheckPrefixLookups(params))
+ .withDefaultLookups(fetchAndCheckDefaultLookups(params))
+ .create();
}
/**
@@ -362,24 +374,165 @@ public class BasicBuilderParameters impl
*
* @param params the map with parameters to be passed to the caller
*/
- public static void createDefensiveCopies(HashMap<String, Object> params)
+ private static void createDefensiveCopies(HashMap<String, Object> params)
{
- // This is safe to case because we have full control over the map
- // and thus know the types of the contained values
- @SuppressWarnings("unchecked")
Map<String, ? extends Lookup> prefixLookups =
- (Map<String, ? extends Lookup>) params.get(PROP_PREFIX_LOOKUPS);
+ fetchPrefixLookups(params);
if (prefixLookups != null)
{
params.put(PROP_PREFIX_LOOKUPS, new HashMap<String, Lookup>(
prefixLookups));
}
+ Collection<? extends Lookup> defLookups = fetchDefaultLookups(params);
+ if (defLookups != null)
+ {
+ params.put(PROP_DEFAULT_LOOKUPS, new ArrayList<Lookup>(defLookups));
+ }
+ }
+
+ /**
+ * Obtains the map with prefix lookups from the parameters map.
+ *
+ * @param params the map with parameters
+ * @return the map with prefix lookups (may be <b>null</b>)
+ */
+ private static Map<String, ? extends Lookup> fetchPrefixLookups(
+ Map<String, Object> params)
+ {
+ // This is safe to cast because we either have full control over the map
+ // and thus know the types of the contained values or have checked
+ // the content before
+ @SuppressWarnings("unchecked")
+ Map<String, ? extends Lookup> prefixLookups =
+ (Map<String, ? extends Lookup>) params.get(PROP_PREFIX_LOOKUPS);
+ return prefixLookups;
+ }
+
+ /**
+ * Tests whether the passed in map with parameters contains a map with
+ * prefix lookups. This method is used if the parameters map is from an
+ * insecure source and we cannot be sure that it contains valid data.
+ * Therefore, we have to map that the key for the prefix lookups actually
+ * points to a map containing keys and values of expected data types.
+ *
+ * @param params the parameters map
+ * @return the obtained map with prefix lookups
+ * @throws IllegalArgumentException if the map contains invalid data
+ */
+ private static Map<String, ? extends Lookup> fetchAndCheckPrefixLookups(
+ Map<String, Object> params)
+ {
+ Map<?, ?> prefixes =
+ fetchParameter(params, PROP_PREFIX_LOOKUPS, Map.class);
+ if (prefixes == null)
+ {
+ return null;
+ }
+
+ for (Map.Entry<?, ?> e : prefixes.entrySet())
+ {
+ if (!(e.getKey() instanceof String)
+ || !(e.getValue() instanceof Lookup))
+ {
+ throw new IllegalArgumentException(
+ "Map with prefix lookups contains invalid data: "
+ + prefixes);
+ }
+ }
+ return fetchPrefixLookups(params);
+ }
+
+ /**
+ * Obtains the collection with default lookups from the parameters map.
+ *
+ * @param params the map with parameters
+ * @return the collection with default lookups (may be <b>null</b>)
+ */
+ private static Collection<? extends Lookup> fetchDefaultLookups(
+ Map<String, Object> params)
+ {
+ // This is safe to cast because we either have full control over the map
+ // and thus know the types of the contained values or have checked
+ // the content before
@SuppressWarnings("unchecked")
Collection<? extends Lookup> defLookups =
(Collection<? extends Lookup>) params.get(PROP_DEFAULT_LOOKUPS);
- if (defLookups != null)
+ return defLookups;
+ }
+
+ /**
+ * Tests whether the passed in map with parameters contains a valid
+ * collection with default lookups. This method works like
+ * {@link #fetchAndCheckPrefixLookups(Map)}, but tests the default lookups
+ * collection.
+ *
+ * @param params the map with parameters
+ * @return the collection with default lookups (may be <b>null</b>)
+ * @throws IllegalArgumentException if invalid data is found
+ */
+ private static Collection<? extends Lookup> fetchAndCheckDefaultLookups(
+ Map<String, Object> params)
+ {
+ Collection<?> col =
+ fetchParameter(params, PROP_DEFAULT_LOOKUPS, Collection.class);
+ if (col == null)
{
- params.put(PROP_DEFAULT_LOOKUPS, new ArrayList<Lookup>(defLookups));
+ return null;
+ }
+
+ for (Object o : col)
+ {
+ if (!(o instanceof Lookup))
+ {
+ throw new IllegalArgumentException(
+ "Collection with default lookups contains invalid data: "
+ + col);
+ }
+ }
+ return fetchDefaultLookups(params);
+ }
+
+ /**
+ * Obtains a parameter from a map and performs a type check.
+ *
+ * @param params the map with parameters
+ * @param key the key of the parameter
+ * @param expClass the expected class of the parameter value
+ * @param <T> the parameter type
+ * @return the value of the parameter in the correct data type
+ * @throws IllegalArgumentException if the parameter is not of the expected
+ * type
+ */
+ private static <T> T fetchParameter(Map<String, Object> params, String key,
+ Class<T> expClass)
+ {
+ Object value = params.get(key);
+ if (value == null)
+ {
+ return null;
+ }
+ if (!expClass.isInstance(value))
+ {
+ throw new IllegalArgumentException(String.format(
+ "Parameter %s is not of type %s!", key,
+ expClass.getSimpleName()));
+ }
+ return expClass.cast(value);
+ }
+
+ /**
+ * Checks whether a map with parameters is present. Throws an exception if
+ * not.
+ *
+ * @param params the map with parameters to check
+ * @throws IllegalArgumentException if the map is <b>null</b>
+ */
+ private static void checkParameters(Map<String, Object> params)
+ {
+ if (params == null)
+ {
+ throw new IllegalArgumentException(
+ "Parameters map must not be null!");
}
}
}
Modified: 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=1438718&r1=1438717&r2=1438718&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/MultiFileConfigurationBuilder.java Fri Jan 25 21:24:42 2013
@@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
@@ -34,6 +35,7 @@ import org.apache.commons.configuration.
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.InterpolatorSpecification;
import org.apache.commons.lang3.concurrent.ConcurrentUtils;
/**
@@ -87,6 +89,10 @@ public class MultiFileConfigurationBuild
private final ConcurrentMap<String, FileBasedConfigurationBuilder<T>> managedBuilders =
new ConcurrentHashMap<String, FileBasedConfigurationBuilder<T>>();
+ /** Stores the {@code ConfigurationInterpolator} object. */
+ private final AtomicReference<ConfigurationInterpolator> interpolator =
+ new AtomicReference<ConfigurationInterpolator>();
+
/**
* A specialized builder listener which gets registered at all managed
* builders. This listener just propagates notifications from managed
@@ -180,7 +186,7 @@ public class MultiFileConfigurationBuild
{
throw new ConfigurationException("No file name pattern is set!");
}
- String fileName = constructFileName(params, multiParams);
+ String fileName = constructFileName(multiParams);
FileBasedConfigurationBuilder<T> builder =
getManagedBuilders().get(fileName);
@@ -280,25 +286,73 @@ public class MultiFileConfigurationBuild
b.removeBuilderListener(managedBuilderDelegationListener);
}
getManagedBuilders().clear();
+ interpolator.set(null);
super.resetParameters();
}
/**
+ * Returns the {@code ConfigurationInterpolator} used by this instance. This
+ * is the object used for evaluating the file name pattern. It is created on
+ * demand.
+ *
+ * @return the {@code ConfigurationInterpolator}
+ */
+ protected ConfigurationInterpolator getInterpolator()
+ {
+ ConfigurationInterpolator result;
+ boolean done;
+
+ // This might create multiple instances under high load,
+ // however, always the same instance is returned.
+ do
+ {
+ result = interpolator.get();
+ if (result != null)
+ {
+ done = true;
+ }
+ else
+ {
+ result = createInterpolator();
+ done = interpolator.compareAndSet(null, result);
+ }
+ } while (!done);
+
+ return result;
+ }
+
+ /**
+ * Creates the {@code ConfigurationInterpolator} to be used by this
+ * instance. This method is called when a file name is to be constructed,
+ * but no current {@code ConfigurationInterpolator} instance is available.
+ * It obtains an instance from this builder's parameters. If no properties
+ * of the {@code ConfigurationInterpolator} are specified in the parameters,
+ * a default instance without lookups is returned (which is probably not
+ * very helpful).
+ *
+ * @return the {@code ConfigurationInterpolator} to be used
+ */
+ protected ConfigurationInterpolator createInterpolator()
+ {
+ InterpolatorSpecification spec =
+ BasicBuilderParameters
+ .fetchInterpolatorSpecification(getParameters());
+ return ConfigurationInterpolator.fromSpecification(spec);
+ }
+
+ /**
* 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,
+ protected String constructFileName(
MultiFileBuilderParametersImpl multiParams)
{
- ConfigurationInterpolator ci =
- BasicBuilderParameters.fetchInterpolator(paramsMap);
- // TODO Make this more generic, handle missing CI
+ ConfigurationInterpolator ci = getInterpolator();
return String.valueOf(ci.interpolate(multiParams.getFilePattern()));
}
Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicBuilderParameters.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicBuilderParameters.java?rev=1438718&r1=1438717&r2=1438718&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicBuilderParameters.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/TestBasicBuilderParameters.java Fri Jan 25 21:24:42 2013
@@ -30,6 +30,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.InterpolatorSpecification;
import org.apache.commons.configuration.interpol.Lookup;
import org.apache.commons.logging.Log;
import org.easymock.EasyMock;
@@ -278,28 +279,124 @@ public class TestBasicBuilderParameters
}
/**
- * Tests fetchInterpolator() if the map does not contain an object.
+ * Tests whether a specification object for interpolation can be obtained.
*/
@Test
- public void testFetchInterpolatorNotFound()
+ public void testFetchInterpolatorSpecification()
{
- Map<String, Object> params = new HashMap<String, Object>();
- assertNull("Got an interpolator",
- BasicBuilderParameters.fetchInterpolator(params));
+ ConfigurationInterpolator parent =
+ EasyMock.createMock(ConfigurationInterpolator.class);
+ Lookup l1 = EasyMock.createMock(Lookup.class);
+ Lookup l2 = EasyMock.createMock(Lookup.class);
+ Lookup l3 = EasyMock.createMock(Lookup.class);
+ Map<String, Lookup> prefixLookups = new HashMap<String, Lookup>();
+ prefixLookups.put("p1", l1);
+ prefixLookups.put("p2", l2);
+ Collection<Lookup> defLookups = Collections.singleton(l3);
+ params.setParentInterpolator(parent);
+ params.setPrefixLookups(prefixLookups);
+ params.setDefaultLookups(defLookups);
+ Map<String, Object> map = params.getParameters();
+ InterpolatorSpecification spec =
+ BasicBuilderParameters.fetchInterpolatorSpecification(map);
+ assertSame("Wrong parent", parent, spec.getParentInterpolator());
+ assertEquals("Wrong prefix lookups", prefixLookups,
+ spec.getPrefixLookups());
+ assertEquals("Wrong number of default lookups", 1, spec
+ .getDefaultLookups().size());
+ assertTrue("Wrong default lookup", spec.getDefaultLookups()
+ .contains(l3));
}
/**
- * Tests whether a {@code ConfigurationInterpolator} can be obtained from a
- * parameters map.
+ * Tests whether an InterpolatorSpecification can be fetched if a
+ * ConfigurationInterpolator is present.
*/
@Test
- public void testFetchInterpolatorFound()
+ public void testFetchInterpolatorSpecificationWithInterpolator()
{
- ConfigurationInterpolator ci = new ConfigurationInterpolator();
+ ConfigurationInterpolator ci =
+ EasyMock.createMock(ConfigurationInterpolator.class);
params.setInterpolator(ci);
- Map<String, Object> map = params.getParameters();
- assertSame("Wrong interpolator", ci,
- BasicBuilderParameters.fetchInterpolator(map));
+ InterpolatorSpecification spec =
+ BasicBuilderParameters.fetchInterpolatorSpecification(params
+ .getParameters());
+ assertSame("Wrong interpolator", ci, spec.getInterpolator());
+ assertNull("Got a parent", spec.getParentInterpolator());
+ }
+
+ /**
+ * Tests fetchInterpolatorSpecification() if the map contains a property of
+ * an invalid data type.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testFetchInterpolatorSpecificationInvalidDataType()
+ {
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("interpolator", this);
+ BasicBuilderParameters.fetchInterpolatorSpecification(map);
+ }
+
+ /**
+ * Tests fetchInterpolatorSpecification() if the map with prefix lookups
+ * contains an invalid key.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testFetchInterpolatorSpecificationInvalidMapKey()
+ {
+ Map<String, Object> map = new HashMap<String, Object>();
+ Map<Object, Object> prefix = new HashMap<Object, Object>();
+ prefix.put(42, EasyMock.createMock(Lookup.class));
+ map.put("prefixLookups", prefix);
+ BasicBuilderParameters.fetchInterpolatorSpecification(map);
+ }
+
+ /**
+ * Tests fetchInterpolatorSpecification() if the map with prefix lookups
+ * contains an invalid value.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testFetchInterpolatorSpecificationInvalidMapValue()
+ {
+ Map<String, Object> map = new HashMap<String, Object>();
+ Map<Object, Object> prefix = new HashMap<Object, Object>();
+ prefix.put("test", this);
+ map.put("prefixLookups", prefix);
+ BasicBuilderParameters.fetchInterpolatorSpecification(map);
+ }
+
+ /**
+ * Tests fetchInterpolatorSpecification() if the collection with default
+ * lookups contains an invalid value.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testFetchInterpolatorSpecificationInvalidCollectionValue()
+ {
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("defaultLookups", Collections.singleton("not a lookup"));
+ BasicBuilderParameters.fetchInterpolatorSpecification(map);
+ }
+
+ /**
+ * Tests that an empty map does not cause any problems.
+ */
+ @Test
+ public void testFetchInterpolatorSpecificationEmpty()
+ {
+ InterpolatorSpecification spec =
+ BasicBuilderParameters.fetchInterpolatorSpecification(params
+ .getParameters());
+ assertNull("Got an interpolator", spec.getInterpolator());
+ assertTrue("Got lookups", spec.getDefaultLookups().isEmpty());
+ }
+
+ /**
+ * Tries to obtain an {@code InterpolatorSpecification} from a null map.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testFetchInterpolatorSpecificationNull()
+ {
+ BasicBuilderParameters.fetchInterpolatorSpecification(null);
}
/**
Modified: 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=1438718&r1=1438717&r2=1438718&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestMultiFileConfigurationBuilder.java Fri Jan 25 21:24:42 2013
@@ -18,6 +18,7 @@ package org.apache.commons.configuration
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -25,6 +26,7 @@ import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
@@ -36,6 +38,7 @@ import org.apache.commons.configuration.
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;
@@ -119,6 +122,30 @@ public class TestMultiFileConfigurationB
}
/**
+ * Tests whether a {@code ConfigurationInterpolator} is created from
+ * properties defined in the parameters object if necessary.
+ */
+ @Test
+ public void testInterpolatorFromParameters() throws ConfigurationException
+ {
+ BasicBuilderParameters params =
+ new MultiFileBuilderParametersImpl().setFilePattern(PATTERN)
+ .setPrefixLookups(
+ Collections.singletonMap(
+ DefaultLookups.SYSTEM_PROPERTIES
+ .getPrefix(),
+ DefaultLookups.SYSTEM_PROPERTIES
+ .getLookup()));
+ MultiFileConfigurationBuilder<XMLConfiguration> builder =
+ new MultiFileConfigurationBuilder<XMLConfiguration>(
+ XMLConfiguration.class);
+ builder.configure(params);
+ switchToConfig(1);
+ assertEquals("Wrong property", 15,
+ builder.getConfiguration().getInt("rowsPerPage"));
+ }
+
+ /**
* Tests whether a managed configuration is properly initialized.
*/
@Test
@@ -320,6 +347,25 @@ public class TestMultiFileConfigurationB
}
/**
+ * Tests whether the ConfigurationInterpolator is reset, too.
+ */
+ @Test
+ public void testInterpolatorReset()
+ {
+ BasicBuilderParameters params =
+ new MultiFileBuilderParametersImpl().setFilePattern(PATTERN);
+ MultiFileConfigurationBuilder<XMLConfiguration> builder =
+ new MultiFileConfigurationBuilder<XMLConfiguration>(
+ XMLConfiguration.class);
+ builder.configure(params);
+ ConfigurationInterpolator interpolator = builder.getInterpolator();
+ assertNotNull("No interpolator", interpolator);
+ builder.resetParameters();
+ assertNotSame("No new interpolator", interpolator,
+ builder.getInterpolator());
+ }
+
+ /**
* Tests whether builder listeners are handled correctly.
*/
@Test