You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by eb...@apache.org on 2009/06/23 18:30:54 UTC

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

Author: ebourg
Date: Tue Jun 23 16:30:53 2009
New Revision: 787743

URL: http://svn.apache.org/viewvc?rev=787743&view=rev
Log:
Ported the changes on CombinedConfiguration from the trunk

Modified:
    commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java
    commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java

Modified: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java?rev=787743&r1=787742&r2=787743&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java Tue Jun 23 16:30:53 2009
@@ -30,6 +30,7 @@
 import org.apache.commons.configuration2.ConfigurationUtils;
 import org.apache.commons.configuration2.event.ConfigurationEvent;
 import org.apache.commons.configuration2.event.ConfigurationListener;
+import org.apache.commons.configuration2.expr.ExpressionEngine;
 import org.apache.commons.configuration2.expr.NodeHandler;
 import org.apache.commons.configuration2.expr.NodeList;
 import org.apache.commons.configuration2.expr.def.DefaultConfigurationKey;
@@ -199,6 +200,12 @@
     /** Stores a map with the named configurations. */
     private Map<String, Configuration> namedConfigurations;
 
+    /**
+     * An expression engine used for converting child configurations to
+     * hierarchical ones.
+     */
+    private ExpressionEngine conversionExpressionEngine;
+
     /** A flag whether an enhanced reload check is to be performed. */
     private boolean forceReloadCheck;
 
@@ -287,6 +294,39 @@
     }
 
     /**
+     * Returns the <code>ExpressionEngine</code> for converting flat child
+     * configurations to hierarchical ones.
+     *
+     * @return the conversion expression engine
+     * @since 1.6
+     */
+    public ExpressionEngine getConversionExpressionEngine()
+    {
+        return conversionExpressionEngine;
+    }
+
+    /**
+     * Sets the <code>ExpressionEngine</code> for converting flat child
+     * configurations to hierarchical ones. When constructing the root node for
+     * this combined configuration the properties of all child configurations
+     * must be combined to a single hierarchical node structure. In this
+     * process, non hierarchical configurations are converted to hierarchical
+     * ones first. This can be problematic if a child configuration contains
+     * keys that are no compatible with the default expression engine used by
+     * hierarchical configurations. Therefore it is possible to specify a
+     * specific expression engine to be used for this purpose.
+     *
+     * @param conversionExpressionEngine the conversion expression engine
+     * @see ConfigurationUtils#convertToHierarchical(Configuration, ExpressionEngine)
+     * @since 1.6
+     */
+    public void setConversionExpressionEngine(
+            ExpressionEngine conversionExpressionEngine)
+    {
+        this.conversionExpressionEngine = conversionExpressionEngine;
+    }
+
+    /**
      * Adds a new configuration to this combined configuration. It is possible
      * (but not mandatory) to give the new configuration a name. This name must
      * be unique, otherwise a <code>ConfigurationRuntimeException</code> will
@@ -394,7 +434,41 @@
      */
     public Configuration getConfiguration(String name)
     {
-        return (Configuration) namedConfigurations.get(name);
+        return namedConfigurations.get(name);
+    }
+
+    /**
+     * Returns a List of all the configurations that have been added.
+     * 
+     * @return A List of all the configurations.
+     * @since 1.7
+     */
+    public List<AbstractHierarchicalConfiguration> getConfigurations()
+    {
+        List<AbstractHierarchicalConfiguration> list = new ArrayList<AbstractHierarchicalConfiguration>();
+        for (ConfigData<?> configuration : configurations)
+        {
+            list.add(configuration.getConfiguration());
+        }
+        return list;
+    }
+
+    /**
+     * Returns a List of the names of all the configurations that have been
+     * added in the order they were added. A NULL value will be present in
+     * the list for each configuration that was added without a name.
+     * 
+     * @return A List of all the configuration names.
+     * @since 1.7
+     */
+    public List<String> getConfigurationNameList()
+    {
+        List<String> list = new ArrayList<String>();
+        for (ConfigData<?> configuration : configurations)
+        {
+            list.add((configuration).getName());
+        }
+        return list;
     }
 
     /**
@@ -785,7 +859,7 @@
      *
      * @param <T> the type of the nodes used by the represented configuration
      */
-    static class ConfigData<T>
+    class ConfigData<T>
     {
         /** Stores a reference to the configuration. */
         private AbstractHierarchicalConfiguration<T> configuration;
@@ -884,7 +958,7 @@
                 }
             }
 
-            T root = getConfiguration().getRootNode();
+            T root = (T) ConfigurationUtils.convertToHierarchical(getConfiguration(), getConversionExpressionEngine()).getRootNode();
 
             // Copy data of the root node to the new path
             atParent.appendChildren(root, getConfiguration().getNodeHandler());

Modified: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java?rev=787743&r1=787742&r2=787743&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java Tue Jun 23 16:30:53 2009
@@ -20,13 +20,17 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
-
+import org.apache.commons.configuration2.AbstractConfiguration;
 import org.apache.commons.configuration2.AbstractHierarchicalConfiguration;
 import org.apache.commons.configuration2.ConfigurationAssert;
 import org.apache.commons.configuration2.ConfigurationException;
@@ -37,6 +41,7 @@
 import org.apache.commons.configuration2.XMLConfiguration;
 import org.apache.commons.configuration2.event.ConfigurationEvent;
 import org.apache.commons.configuration2.event.ConfigurationListener;
+import org.apache.commons.configuration2.expr.def.DefaultExpressionEngine;
 import org.apache.commons.configuration2.reloading.FileAlwaysReloadingStrategy;
 
 /**
@@ -59,13 +64,16 @@
     private static final String CHILD2 = TEST_NAME + "2";
 
     /** Constant for the name of the XML reload test file.*/
-    private static final String RELOAD_NAME1 = "reload.xml";
+    private static final String RELOAD_XML_NAME = "reload.xml";
 
     /** Constant for the content of a XML reload test file.*/
-    private static final String RELOAD_CONTENT = "<xml><xmlReload>%d</xmlReload></xml>";
+    private static final String RELOAD_XML_CONTENT = "<xml><xmlReload>%d</xmlReload></xml>";
 
     /** Constant for the name of the properties reload test file.*/
-    private static final String RELOAD_NAME2 = "reload2.xml";
+    private static final String RELOAD_PROPS_NAME = "reload2.xml";
+
+    /** Constant for the content of a properties reload test file.*/
+    private static final String RELOAD_PROPS_CONTENT = "propsReload = {0}";
 
     /** Constant for the directory for writing test files.*/
     private static final File TEST_DIR = new File("target");
@@ -79,7 +87,6 @@
     /** The test event listener. */
     private CombinedListener listener;
 
-    @Override
     protected void setUp() throws Exception
     {
         super.setUp();
@@ -92,7 +99,6 @@
      * Performs clean-up after a test run. If test files have been created, they
      * are removed now.
      */
-    @Override
     protected void tearDown() throws Exception
     {
         if (testFiles != null)
@@ -399,6 +405,7 @@
     {
         config.addConfiguration(setUpTestConfiguration());
         config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
+        //config.addConfiguration(new PropertiesConfiguration(), "props");
 
         CombinedConfiguration cc2 = (CombinedConfiguration) config.clone();
         assertEquals("Wrong number of contained configurations", config
@@ -465,8 +472,8 @@
         final String prefix1 = "reload1";
         final String prefix2 = "reload2";
         config.setForceReloadCheck(true);
-        File testXmlFile = writeReloadFile(RELOAD_NAME1, 0);
-        File testXmlFile2 = writeReloadFile(RELOAD_NAME2, 0);
+        File testXmlFile = writeReloadFile(RELOAD_XML_NAME, 0);
+        File testXmlFile2 = writeReloadFile(RELOAD_PROPS_NAME, 0);
         XMLConfiguration c1 = new XMLConfiguration(testXmlFile);
         c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
         XMLConfiguration c2 = new XMLConfiguration(testXmlFile2);
@@ -479,11 +486,11 @@
         assertEquals("Wrong xml 2 reload value", 0, config.getInt(prefix2
                 + ".xmlReload"));
 
-        writeReloadFile(RELOAD_NAME1, 1);
+        writeReloadFile(RELOAD_XML_NAME, 1);
         assertEquals("XML reload 1 not detected", 1, config.getInt(prefix1
                 + ".xmlReload"));
         config.setForceReloadCheck(false);
-        writeReloadFile(RELOAD_NAME2, 1);
+        writeReloadFile(RELOAD_PROPS_NAME, 1);
         assertEquals("XML 2 reload detected though check flag is false", 0,
                 config.getInt(prefix2 + ".xmlReload"));
     }
@@ -496,13 +503,13 @@
             ConfigurationException
     {
         config.setForceReloadCheck(true);
-        File testXmlFile = writeReloadFile(RELOAD_NAME1, 0);
+        File testXmlFile = writeReloadFile(RELOAD_XML_NAME, 0);
         XMLConfiguration c1 = new XMLConfiguration(testXmlFile);
         c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
         final String prefix = "reloadCheck";
         config.addConfiguration(c1, CHILD1, prefix);
         SubConfiguration<?> sub = config.configurationAt(prefix, true);
-        writeReloadFile(RELOAD_NAME1, 1);
+        writeReloadFile(RELOAD_XML_NAME, 1);
         assertEquals("Reload not detected", 1, sub.getInt("xmlReload"));
     }
 
@@ -632,6 +639,138 @@
     }
 
     /**
+     * Tests using a conversion expression engine for child configurations with
+     * strange keys. This test is related to CONFIGURATION-336.
+     */
+    public void testConversionExpressionEngine()
+    {
+        InMemoryConfiguration child = new InMemoryConfiguration();
+        DefaultExpressionEngine engineInit = new DefaultExpressionEngine();
+        engineInit.setIndexStart("{");
+        engineInit.setIndexEnd("}");
+        child.setExpressionEngine(engineInit);
+        
+        child.addProperty("test(a)", "1,2,3");
+        
+        config.addConfiguration(child);
+        DefaultExpressionEngine engineQuery = new DefaultExpressionEngine();
+        engineQuery.setIndexStart("<");
+        engineQuery.setIndexEnd(">");
+        config.setExpressionEngine(engineQuery);
+        DefaultExpressionEngine engineConvert = new DefaultExpressionEngine();
+        engineConvert.setIndexStart("[");
+        engineConvert.setIndexEnd("]");
+        config.setConversionExpressionEngine(engineConvert);
+        assertEquals("Wrong property 1", "1", config.getString("test(a)<0>"));
+        assertEquals("Wrong property 2", "2", config.getString("test(a)<1>"));
+        assertEquals("Wrong property 3", "3", config.getString("test(a)<2>"));
+    }
+
+    /**
+     * Tests whether reload operations can cause a deadlock when the combined
+     * configuration is accessed concurrently. This test is related to
+     * CONFIGURATION-344.
+     */
+    /* todo test failing due to the lack of synchronization on the hierarchical configurations (CONFIGURATION-390) 
+    public void testDeadlockWithReload() throws ConfigurationException,
+            InterruptedException
+    {
+        final XMLConfiguration child = new XMLConfiguration(
+                "test.xml");
+        child.setReloadingStrategy(new FileAlwaysReloadingStrategy());
+        config.addConfiguration(child);
+        final int count = 1000;
+        
+        assertEquals("Wrong value of combined property", 8, config.getInt("test.short"));
+
+        class ReloadThread extends Thread
+        {
+            boolean error = false;
+
+            public void run()
+            {
+                for (int i = 0; i < count && !error; i++)
+                {
+                    try
+                    {
+                        if (!child.containsKey("test.short"))
+                        {
+                            error = true;
+                        }
+                    }
+                    catch (NoSuchElementException nsex)
+                    {
+                        error = true;
+                    }
+                }
+            }
+        }
+
+        ReloadThread reloadThread = new ReloadThread();
+        reloadThread.start();
+        for (int i = 0; i < count; i++)
+        {
+            assertEquals("Wrong value of combined property", 8, config.getInt("test.short"));
+        }
+        reloadThread.join();
+        assertFalse("Failure in thread", reloadThread.error);
+    }
+    */
+
+    public void testGetConfigurations() throws Exception
+    {
+        config.addConfiguration(setUpTestConfiguration());
+        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
+        AbstractHierarchicalConfiguration pc = new XMLConfiguration();
+        config.addConfiguration(pc, "props");
+        List list = config.getConfigurations();
+        assertNotNull("No list of configurations returned", list);
+        assertTrue("Incorrect number of configurations", list.size() == 3);
+        AbstractConfiguration c = ((AbstractConfiguration)list.get(2));
+        assertTrue("Incorrect configuration", c == pc);
+    }
+
+    public void testGetConfigurationNameList() throws Exception
+    {
+        config.addConfiguration(setUpTestConfiguration());
+        config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
+        AbstractHierarchicalConfiguration pc = new XMLConfiguration();
+        config.addConfiguration(pc, "props");
+        List list = config.getConfigurationNameList();
+        assertNotNull("No list of configurations returned", list);
+        assertTrue("Incorrect number of configurations", list.size() == 3);
+        String name = ((String)list.get(1));
+        assertNotNull("No name returned", name);
+        assertTrue("Incorrect configuration name", TEST_NAME.equals(name));
+    }
+
+    /**
+     * Tests whether changes on a sub node configuration that is part of a
+     * combined configuration are detected. This test is related to
+     * CONFIGURATION-368.
+     */
+    public void testReloadWithSubNodeConfig() throws Exception
+    {
+        final String reloadContent = "<config><default><xmlReload1>{0}</xmlReload1></default></config>";
+        config.setForceReloadCheck(true);
+        config.setNodeCombiner(new OverrideCombiner());
+        File testXmlFile1 = writeReloadFile(RELOAD_XML_NAME, reloadContent, 0);
+        final String prefix1 = "default";
+        XMLConfiguration c1 = new XMLConfiguration(testXmlFile1);
+        SubConfiguration sub1 = c1.configurationAt(prefix1, true);
+        assertEquals("Inital test for sub config 1 failed", 0, sub1
+                .getInt("xmlReload1"));
+        config.addConfiguration(sub1);
+        assertEquals(
+                "Could not get value for sub config 1 from combined config", 0,
+                config.getInt("xmlReload1"));
+        c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
+        writeReloadFile(RELOAD_XML_NAME, reloadContent, 1);
+        assertEquals("Reload of sub config 1 not detected", 1, config
+                .getInt("xmlReload1"));
+    }
+    
+    /**
      * Tests a combined configuration that is contained in another combined
      * configuration.
      */
@@ -708,7 +847,24 @@
     private File writeReloadFile(String name, int value)
             throws IOException
     {
-        return writeFile(name, String.format(RELOAD_CONTENT, value));
+        return writeFile(name, String.format(RELOAD_XML_CONTENT, value));
+    }
+
+    /**
+     * Writes a file for testing reload operations.
+     *
+     * @param name the name of the reload test file
+     * @param content the content of the file
+     * @param value the value of the reload test property
+     * @return the file that was written
+     * @throws IOException if an error occurs
+     */
+    private File writeReloadFile(String name, String content, int value)
+            throws IOException
+    {
+        return writeFile(name, MessageFormat.format(content, new Object[] {
+            new Integer(value)
+        }));
     }
 
     /**