commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r1405890 - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/ test/java/org/apache/commons/configuration/
Date Mon, 05 Nov 2012 17:30:20 GMT
Author: oheger
Date: Mon Nov  5 17:30:20 2012
New Revision: 1405890

URL: http://svn.apache.org/viewvc?rev=1405890&view=rev
Log:
Added a utility method to ConfigurationUtils to create immutable configurations.

Added:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ImmutableConfigurationInvocationHandler.java
  (with props)
    commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestImmutableConfiguration.java
  (with props)
Modified:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationUtils.java

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationUtils.java?rev=1405890&r1=1405889&r2=1405890&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationUtils.java
(original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationUtils.java
Mon Nov  5 17:30:20 2012
@@ -23,6 +23,7 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Iterator;
@@ -62,6 +63,14 @@ public final class ConfigurationUtils
     /** Constant for parsing numbers in hex format. */
     private static final int HEX = 16;
 
+    /**
+     * An array with interfaces to be implemented by a proxy for an immutable
+     * configuration.
+     */
+    private static final Class<?>[] IMMUTABLE_CONFIG_IFCS = {
+        ImmutableConfiguration.class
+    };
+
     /** The logger.*/
     private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class);
 
@@ -755,4 +764,30 @@ public final class ConfigurationUtils
             }
         });
     }
+
+    /**
+     * Creates an {@code ImmutableConfiguration} from the given
+     * {@code Configuration} object. This method creates a proxy object wrapping
+     * the original configuration and making it available under the
+     * {@code ImmutableConfiguration} interface. Through this interface the
+     * configuration cannot be manipulated. It is also not possible to cast the
+     * returned object back to a {@code Configuration} instance to circumvent
+     * this protection.
+     *
+     * @param c the {@code Configuration} to be wrapped (must not be
+     *        <b>null</b>)
+     * @return an {@code ImmutableConfiguration} view on the specified
+     *         {@code Configuration} object
+     * @throws NullPointerException if the passed in {@code Configuration} is
+     *         <b>null</b>
+     * @since 2.0
+     */
+    public static ImmutableConfiguration unmodifiableConfiguration(
+            Configuration c)
+    {
+        return (ImmutableConfiguration) Proxy.newProxyInstance(
+                ConfigurationUtils.class.getClassLoader(),
+                IMMUTABLE_CONFIG_IFCS,
+                new ImmutableConfigurationInvocationHandler(c));
+    }
 }

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ImmutableConfigurationInvocationHandler.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ImmutableConfigurationInvocationHandler.java?rev=1405890&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ImmutableConfigurationInvocationHandler.java
(added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ImmutableConfigurationInvocationHandler.java
Mon Nov  5 17:30:20 2012
@@ -0,0 +1,146 @@
+/*
+ * 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;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A specialized {@code InvocationHandler} implementation for supporting
+ * immutable configurations.
+ * </p>
+ * <p>
+ * An instance of this class is constructed with a reference to a
+ * {@code Configuration} object. All method invocations (which stem from the
+ * {@code ImmutableConfiguration} interface) are delegated to this object. That
+ * way all functionality is actually backed by the underlying
+ * {@code Configuration} implementation, but because the associated proxy only
+ * implements the {@code ImmutableConfiguration} interface manipulations are not
+ * possible.
+ * </p>
+ * <p>
+ * There is one caveat however: Some methods of the
+ * {@code ImmutableConfiguration} interface return an {@code Iterator} object.
+ * Using the iterator's {@code remove()} method it may be possible to remove
+ * keys from the underlying {@code Configuration} object. Therefore, in these
+ * cases a specialized {@code Iterator} is returned which does not support the
+ * remove operation.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+class ImmutableConfigurationInvocationHandler implements InvocationHandler
+{
+    /** The underlying configuration object. */
+    private final Configuration wrappedConfiguration;
+
+    /**
+     * Creates a new instance of {@code ImmutableConfigurationInvocationHandler}
+     * and initializes it with the wrapped configuration object.
+     *
+     * @param conf the wrapped {@code Configuration} (must not be <b>null</b>)
+     * @throws NullPointerException if the {@code Configuration} is <b>null</b>
+     */
+    public ImmutableConfigurationInvocationHandler(Configuration conf)
+    {
+        if (conf == null)
+        {
+            throw new NullPointerException(
+                    "Wrapped configuration must not be null!");
+        }
+        wrappedConfiguration = conf;
+    }
+
+    /**
+     * {@inheritDoc} This implementation delegates to the wrapped configuration
+     * object. Result objects are wrapped if necessary.
+     */
+    public Object invoke(Object proxy, Method method, Object[] args)
+            throws Throwable
+    {
+        return handleResult(method.invoke(wrappedConfiguration, args));
+    }
+
+    /**
+     * Handles the result from the method invocation on the wrapped
+     * configuration. This implementation wraps result objects if necessary so
+     * that the underlying configuration cannot be manipulated.
+     *
+     * @param result the result object
+     * @return the processed result object
+     */
+    private static Object handleResult(Object result)
+    {
+        if (result instanceof Iterator)
+        {
+            return new ImmutableIterator((Iterator<?>) result);
+        }
+        return result;
+    }
+
+    /**
+     * A specialized {@code Iterator} implementation which delegates to an
+     * underlying iterator, but does not support the {@code remove()} method.
+     */
+    private static class ImmutableIterator implements Iterator<Object>
+    {
+        /** The underlying iterator. */
+        private final Iterator<?> wrappedIterator;
+
+        /**
+         * Creates a new instance of {@code ImmutableIterator} and sets the
+         * underlying iterator.
+         *
+         * @param it the underlying iterator
+         */
+        public ImmutableIterator(Iterator<?> it)
+        {
+            wrappedIterator = it;
+        }
+
+        /**
+         * {@inheritDoc} This implementation just delegates to the underlying
+         * iterator.
+         */
+        public boolean hasNext()
+        {
+            return wrappedIterator.hasNext();
+        }
+
+        /**
+         * {@inheritDoc} This implementation just delegates to the underlying
+         * iterator.
+         */
+        public Object next()
+        {
+            return wrappedIterator.next();
+        }
+
+        /**
+         * {@inheritDoc} This implementation just throws an exception: removing
+         * objects is not supported.
+         */
+        public void remove()
+        {
+            throw new UnsupportedOperationException(
+                    "remove() operation not supported!");
+        }
+    }
+}

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

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

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

Added: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestImmutableConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestImmutableConfiguration.java?rev=1405890&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestImmutableConfiguration.java
(added)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestImmutableConfiguration.java
Mon Nov  5 17:30:20 2012
@@ -0,0 +1,179 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.commons.configuration.builder.FileBasedBuilderParameters;
+import org.apache.commons.configuration.builder.FileBasedConfigurationBuilder;
+import org.junit.Test;
+
+/**
+ * A test class which tests functionality related to immutable configurations.
+ *
+ * @version $Id$
+ */
+public class TestImmutableConfiguration
+{
+    /** Constant for the name of a test properties file. */
+    private static final String TEST_FILE = "test.properties";
+
+    /**
+     * Tries to create an immutable configuration from a null object.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testUnmodifiableConfigurationNull()
+    {
+        ConfigurationUtils.unmodifiableConfiguration(null);
+    }
+
+    /**
+     * Creates a test configuration object filled with properties.
+     *
+     * @return the test configuration
+     * @throws ConfigurationException if an error occurs
+     */
+    private static PropertiesConfiguration createTestConfig()
+            throws ConfigurationException
+    {
+        return new FileBasedConfigurationBuilder<PropertiesConfiguration>(
+                PropertiesConfiguration.class).configure(
+                new FileBasedBuilderParameters().setFile(ConfigurationAssert
+                        .getTestFile(TEST_FILE))).getConfiguration();
+    }
+
+    /**
+     * Tests whether data can be accessed from an unmodifiable configuration.
+     */
+    @Test
+    public void testUnmodifiableConfigurationAccess()
+            throws ConfigurationException
+    {
+        Configuration confOrg = createTestConfig();
+        ImmutableConfiguration conf =
+                ConfigurationUtils.unmodifiableConfiguration(confOrg);
+        assertFalse("Empty", conf.isEmpty());
+        for (Iterator<String> it = confOrg.getKeys(); it.hasNext();)
+        {
+            String key = it.next();
+            assertTrue("Key not contained: " + key, conf.containsKey(key));
+            assertEquals("Wrong value for " + key, confOrg.getProperty(key),
+                    conf.getProperty(key));
+        }
+    }
+
+    /**
+     * Tests different access methods for properties.
+     */
+    @Test
+    public void testUnmodifiableConfigurationOtherTypes()
+            throws ConfigurationException
+    {
+        ImmutableConfiguration conf =
+                ConfigurationUtils
+                        .unmodifiableConfiguration(createTestConfig());
+        assertEquals("Wrong byte", (byte) 10, conf.getByte("test.byte"));
+        assertEquals("Wrong boolean", true, conf.getBoolean("test.boolean"));
+        assertEquals("Wrong double", 10.25, conf.getDouble("test.double"), .05);
+        assertEquals("Wrong float", 20.25f, conf.getFloat("test.float"), .05);
+        assertEquals("Wrong int", 10, conf.getInt("test.integer"));
+        assertEquals("Wrong long", 1000000L, conf.getLong("test.long"));
+        assertEquals("Wrong short", (short) 1, conf.getShort("test.short"));
+    }
+
+    /**
+     * Obtains all keys from the given iteration.
+     *
+     * @param it the iterator
+     * @return a set with all keys
+     */
+    private static Set<String> fetchKeys(Iterator<String> it)
+    {
+        Set<String> keys = new HashSet<String>();
+        while (it.hasNext())
+        {
+            keys.add(it.next());
+        }
+        return keys;
+    }
+
+    /**
+     * Tests an iteration over the keys of the immutable configuration.
+     */
+    @Test
+    public void testUnmodifiableConfigurationIterate()
+            throws ConfigurationException
+    {
+        Configuration confOrg = createTestConfig();
+        ImmutableConfiguration conf =
+                ConfigurationUtils.unmodifiableConfiguration(confOrg);
+        assertEquals("Different keys", fetchKeys(confOrg.getKeys()),
+                fetchKeys(conf.getKeys()));
+    }
+
+    /**
+     * Tests that it is not possible to remove keys using the iterator.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testUnmodifiableConfigurationIteratorRemove()
+            throws ConfigurationException
+    {
+        ImmutableConfiguration conf =
+                ConfigurationUtils
+                        .unmodifiableConfiguration(createTestConfig());
+        Iterator<String> it = conf.getKeys();
+        it.next();
+        it.remove();
+    }
+
+    /**
+     * Tests whether an update of the original configuration is visible for the
+     * immutable view.
+     */
+    @Test
+    public void testUnmodifiableConfigurationLiveUpdate()
+            throws ConfigurationException
+    {
+        Configuration confOrg = createTestConfig();
+        ImmutableConfiguration conf =
+                ConfigurationUtils.unmodifiableConfiguration(confOrg);
+        final String key = "new.property";
+        String value = "new value";
+        confOrg.addProperty(key, value);
+        assertEquals("Value not set", value, conf.getString(key));
+    }
+
+    /**
+     * Tests that a cast to a mutable configuration is not possible.
+     */
+    @Test(expected = ClassCastException.class)
+    public void testUnmodifiableConfigurationCast()
+            throws ConfigurationException
+    {
+        ImmutableConfiguration conf =
+                ConfigurationUtils
+                        .unmodifiableConfiguration(createTestConfig());
+        Configuration mutableConf = (Configuration) conf;
+        mutableConf.clear();
+    }
+}

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

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

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



Mime
View raw message