You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by rg...@apache.org on 2009/09/30 01:38:53 UTC

svn commit: r820124 - in /commons/proper/configuration/branches/CONFIGURATION_390: ./ conf/ src/java/org/apache/commons/configuration/ src/test/org/apache/commons/configuration/ src/test/org/apache/commons/configuration/reloading/

Author: rgoers
Date: Tue Sep 29 23:38:52 2009
New Revision: 820124

URL: http://svn.apache.org/viewvc?rev=820124&view=rev
Log:
Fix memory leak on reloading. Have DynamicCombinedConfiguration participate in locking

Added:
    commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder4.xml
      - copied, changed from r819418, commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder2.xml
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/Lock.java
Modified:
    commons/proper/configuration/branches/CONFIGURATION_390/pom.xml
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/CombinedConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/HierarchicalReloadableConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/XMLConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/Logging.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestXMLConfiguration.java
    commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/reloading/FileRandomReloadingStrategy.java

Copied: commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder4.xml (from r819418, commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder2.xml)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder4.xml?p2=commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder4.xml&p1=commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder2.xml&r1=819418&r2=820124&rev=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder2.xml (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/conf/testMultiTenentConfigurationBuilder4.xml Tue Sep 29 23:38:52 2009
@@ -4,11 +4,15 @@
   <header>
     <result delimiterParsingDisabled="true" forceReloadCheck="true" loggerName="TestLogger"
             config-class="org.apache.commons.configuration.DynamicCombinedConfiguration"
-            keyPattern="$${sys:Id}">
+            keyPattern="$${test:Id}">
       <nodeCombiner config-class="org.apache.commons.configuration.tree.MergeCombiner"/>
       <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
     </result>
+    <lookups>
+      <lookup config-prefix="test"
+              config-class="org.apache.commons.configuration.TestDynamicCombinedConfiguration$ThreadLookup"/>
+    </lookups>
     <entity-resolver catalogFiles="catalog.xml"/>
     <providers>
       <provider config-tag="multifile"
@@ -17,19 +21,19 @@
     </providers>
   </header>
   <override>
-    <multifile filePattern="testMultiConfiguration_$$${sys:Id}.xml"
+    <multifile filePattern="testMultiConfiguration_$$${test:Id}.xml"
                config-name="clientConfig" delimiterParsingDisabled="true" schemaValidation="true">
        <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
        <reloadingStrategy refreshDelay="500"
-          config-class="org.apache.commons.configuration.reloading.FileChangedReloadingStrategy"/>
+          config-class="org.apache.commons.configuration.reloading.FileRandomReloadingStrategy"/>
     </multifile>
     <xml fileName="testMultiConfiguration_default.xml"
          config-name="defaultConfig" delimiterParsingDisabled="true" schemaValidation="true">
       <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
       <reloadingStrategy refreshDelay="500"
-          config-class="org.apache.commons.configuration.reloading.FileChangedReloadingStrategy"/>
+          config-class="org.apache.commons.configuration.reloading.FileRandomReloadingStrategy"/>
     </xml>
   </override>
 </configuration>
\ No newline at end of file

Modified: commons/proper/configuration/branches/CONFIGURATION_390/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/pom.xml?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/pom.xml (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/pom.xml Tue Sep 29 23:38:52 2009
@@ -486,6 +486,8 @@
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
           <configuration>
+            <!-- Uncomment to enable profiling unit tests -->
+            <!-- <argLine>-agentpath:"${yourkit.home}/bin/mac/libyjpagent.jnilib"</argLine> -->
             <forkMode>once</forkMode>
             <excludes>
               <exclude>**/TestWebdavConfigurationBuilder.java</exclude>
@@ -584,6 +586,14 @@
         </plugins>
       </build>
     </profile>
+    <!-- Uncomment this and set the path accordingly to enable YourKit -->
+    <!-- http://www.yourkit.com/docs/80/help/agent.jsp -->
+    <!-- <profile>
+      <id>yourkit-profile</id>
+      <properties>
+        <yourkit.home>/Applications/YourKit_Java_Profiler_8.0.17.app/</yourkit.home>
+      </properties>
+    </profile> -->
   </profiles>
   <reporting>
     <plugins>

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/AbstractFileConfiguration.java Tue Sep 29 23:38:52 2009
@@ -103,7 +103,7 @@
     protected ReloadingStrategy strategy;
 
     /** A lock object for protecting reload operations.*/
-    protected Object reloadLock = new Object();
+    protected Object reloadLock = new Lock("AbstractFileConfiguration");
 
     /** Stores the encoding of the configuration file.*/
     private String encoding;

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/CombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/CombinedConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/CombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/CombinedConfiguration.java Tue Sep 29 23:38:52 2009
@@ -234,6 +234,18 @@
         clear();
     }
 
+    public CombinedConfiguration(NodeCombiner comb, Lock lock)
+    {
+        super(lock);
+        setNodeCombiner((comb != null) ? comb : DEFAULT_COMBINER);
+        clear();
+    }
+
+    public CombinedConfiguration(Lock lock)
+    {
+        this(null, lock);
+    }
+
     /**
      * Creates a new instance of <code>CombinedConfiguration</code> that uses
      * a union combiner.
@@ -242,7 +254,7 @@
      */
     public CombinedConfiguration()
     {
-        this(null);
+        this(null, null);
     }
 
     /*

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java Tue Sep 29 23:38:52 2009
@@ -32,8 +32,10 @@
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
 import org.apache.commons.configuration.tree.NodeCombiner;
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.commons.lang.text.StrSubstitutor;
 
 /**
  * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
@@ -73,8 +75,12 @@
     /** Stores the combiner. */
     private NodeCombiner nodeCombiner;
 
+    private Lock reloadLock = new Lock("DynamicCombinedConfiguration");
+
     /** The name of the logger to use for each CombinedConfiguration */
-    private String loggerName;
+    private String loggerName = DynamicCombinedConfiguration.class.getName();
+
+    StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
 
     /**
      * Creates a new instance of <code>CombinedConfiguration</code> and
@@ -88,6 +94,7 @@
         super();
         setNodeCombiner(comb);
         setIgnoreReloadExceptions(false);
+        setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
     }
 
     /**
@@ -100,6 +107,7 @@
     {
         super();
         setIgnoreReloadExceptions(false);
+        setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
     }
 
     public void setKeyPattern(String pattern)
@@ -754,14 +762,14 @@
 
     private CombinedConfiguration getCurrentConfig()
     {
-        String key = getSubstitutor().replace(keyPattern);
+        String key = localSubst.replace(keyPattern);
         CombinedConfiguration config;
         synchronized (getNodeCombiner())
         {
             config = (CombinedConfiguration) configs.get(key);
             if (config == null)
             {
-                config = new CombinedConfiguration(getNodeCombiner());
+                config = new CombinedConfiguration(getNodeCombiner(), reloadLock);
                 if (loggerName != null)
                 {
                     Log log = LogFactory.getLog(loggerName);
@@ -798,6 +806,10 @@
                 configs.put(key, config);
             }
         }
+        if (getLogger().isDebugEnabled())
+        {
+            getLogger().debug("Returning config for " + key + ": " + config);
+        }
         return config;
     }
 

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/HierarchicalReloadableConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/HierarchicalReloadableConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/HierarchicalReloadableConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/HierarchicalReloadableConfiguration.java Tue Sep 29 23:38:52 2009
@@ -16,19 +16,21 @@
 {
     private final Object reloadLock;
 
+    private static final String LOCK_NAME = "HierarchicalReloadableConfigurationLock";
+
     /**
      * Creates a new instance of <code>HierarchicalReloadableConfiguration</code>.
      */
     public HierarchicalReloadableConfiguration()
     {
         super();
-        reloadLock = new Object();
+        reloadLock = new Lock(LOCK_NAME);
     }
 
     public HierarchicalReloadableConfiguration(Object lock)
     {
         super();
-        reloadLock = lock == null ? new Object() : lock;
+        reloadLock = lock == null ? new Lock(LOCK_NAME) : lock;
     }
 
     /**
@@ -43,7 +45,7 @@
     public HierarchicalReloadableConfiguration(HierarchicalConfiguration c)
     {
         super(c);
-        reloadLock = new Object();
+        reloadLock = new Lock(LOCK_NAME);
     }
 
 

Added: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/Lock.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/Lock.java?rev=820124&view=auto
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/Lock.java (added)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/Lock.java Tue Sep 29 23:38:52 2009
@@ -0,0 +1,36 @@
+package org.apache.commons.configuration;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: rgoers
+ * Date: Sep 29, 2009
+ * Time: 12:50:36 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class Lock
+{
+    private final String name;
+    private final int instanceId;
+
+    private static String counterLock = "Lock";
+    private static int counter = 0;
+
+    public Lock(String name)
+    {
+        this.name = name;
+        synchronized(counterLock)
+        {
+            instanceId = ++counter;
+        }
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public String toString()
+    {
+        return "Lock: " + name + " id = " + instanceId;
+    }
+}

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java Tue Sep 29 23:38:52 2009
@@ -39,9 +39,11 @@
 import org.apache.commons.configuration.tree.ExpressionEngine;
 import org.apache.commons.configuration.reloading.ReloadingStrategy;
 import org.apache.commons.configuration.resolver.EntityResolverSupport;
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 import org.apache.commons.beanutils.BeanUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.commons.lang.text.StrSubstitutor;
 import org.xml.sax.EntityResolver;
 import org.xml.sax.SAXParseException;
 
@@ -101,6 +103,8 @@
     /** The EntityResolver */
     private EntityResolver entityResolver;
 
+    private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
+
     /**
      * Default Constructor.
      */
@@ -679,7 +683,7 @@
         {
             throw new ConfigurationRuntimeException("File pattern must be defined");
         }
-        String path = getSubstitutor().replace(pattern);
+        String path = localSubst.replace(pattern);
         synchronized (configurationsMap)
         {
             if (configurationsMap.containsKey(path))

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/XMLConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/XMLConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/XMLConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/java/org/apache/commons/configuration/XMLConfiguration.java Tue Sep 29 23:38:52 2009
@@ -567,6 +567,7 @@
     public void clear()
     {
         super.clear();
+        setRoot(new Node());
         document = null;
     }
 

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/Logging.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/Logging.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/Logging.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/Logging.java Tue Sep 29 23:38:52 2009
@@ -59,7 +59,7 @@
         {
             org.apache.log4j.Logger log = org.apache.log4j.Logger.getRootLogger();
             log.setLevel(Level.toLevel(level));
-            Appender appender = new ConsoleAppender(new PatternLayout("%p - %m%n"), ConsoleAppender.SYSTEM_OUT);
+            Appender appender = new ConsoleAppender(new PatternLayout("%p %l - %m%n"), ConsoleAppender.SYSTEM_OUT);
             log.addAppender(appender);
         }
     }

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java Tue Sep 29 23:38:52 2009
@@ -21,12 +21,15 @@
 
 import junit.framework.TestCase;
 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+import org.apache.commons.lang.text.StrLookup;
 
 public class TestDynamicCombinedConfiguration extends TestCase
 {
     private static String PATTERN ="${sys:Id}";
     private static String PATTERN1 = "target/test-classes/testMultiConfiguration_${sys:Id}.xml";
     private static String DEFAULT_FILE = "target/test-classes/testMultiConfiguration_default.xml";
+    private static final File MULTI_TENENT_FILE = new File(
+            "conf/testMultiTenentConfigurationBuilder4.xml");
 
     public void testConfiguration() throws Exception
     {
@@ -57,9 +60,186 @@
         assertEquals("a\\,b\\,c", config.getString("split/list2"));
     }
 
+    public void testConcurrentGetAndReload() throws Exception
+    {
+        final int threadCount = 5;
+        final int loopCount = 500;
+        System.getProperties().remove("Id");
+        DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
+        factory.setFile(MULTI_TENENT_FILE);
+        CombinedConfiguration config = factory.getConfiguration(true);
+
+        assertEquals(config.getString("rowsPerPage"), "50");
+        Thread testThreads[] = new Thread[threadCount];
+        int failures[] = new int[threadCount];
+
+        for (int i = 0; i < testThreads.length; ++i)
+        {
+            testThreads[i] = new ReloadThread(config, failures, i, loopCount, false, null, "50");
+            testThreads[i].start();
+        }
+
+        int totalFailures = 0;
+        for (int i = 0; i < testThreads.length; ++i)
+        {
+            testThreads[i].join();
+            totalFailures += failures[i];
+        }
+        assertTrue(totalFailures + " failures Occurred", totalFailures == 0);
+    }
+
+    public void testConcurrentGetAndReload2() throws Exception
+    {
+        final int threadCount = 5;
+        final int loopCount = 500;
+        System.getProperties().remove("Id");
+        DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
+        factory.setFile(MULTI_TENENT_FILE);
+        CombinedConfiguration config = factory.getConfiguration(true);
+
+        assertEquals(config.getString("rowsPerPage"), "50");
+
+        Thread testThreads[] = new Thread[threadCount];
+        int failures[] = new int[threadCount];
+        System.setProperty("Id", "2002");
+        assertEquals(config.getString("rowsPerPage"), "25");
+        for (int i = 0; i < testThreads.length; ++i)
+        {
+            testThreads[i] = new ReloadThread(config, failures, i, loopCount, false, null, "25");
+            testThreads[i].start();
+        }
+
+        int totalFailures = 0;
+        for (int i = 0; i < testThreads.length; ++i)
+        {
+            testThreads[i].join();
+            totalFailures += failures[i];
+        }
+        System.getProperties().remove("Id");
+        assertTrue(totalFailures + " failures Occurred", totalFailures == 0);
+    }
+
+    public void testConcurrentGetAndReloadMultipleClients() throws Exception
+    {
+        final int threadCount = 5;
+        final int loopCount = 500;
+        System.getProperties().remove("Id");
+        DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
+        factory.setFile(MULTI_TENENT_FILE);
+        CombinedConfiguration config = factory.getConfiguration(true);
+
+        assertEquals(config.getString("rowsPerPage"), "50");
+
+        Thread testThreads[] = new Thread[threadCount];
+        int failures[] = new int[threadCount];
+        String[] ids = new String[] {null, "2002", "3001", "3002", "3003"};
+        String[] expected = new String[] {"50", "25", "15", "25", "50"};
+        for (int i = 0; i < testThreads.length; ++i)
+        {
+            testThreads[i] = new ReloadThread(config, failures, i, loopCount, true, ids[i], expected[i]);
+            testThreads[i].start();
+        }
+
+        int totalFailures = 0;
+        for (int i = 0; i < testThreads.length; ++i)
+        {
+            testThreads[i].join();
+            totalFailures += failures[i];
+        }
+        System.getProperties().remove("Id");
+        if (totalFailures != 0)
+        {
+            System.out.println("Failures:");
+            for (int i = 0; i < testThreads.length; ++i)
+            {
+                System.out.println("Thread " + i + " " + failures[i]);
+            }
+        }
+        assertTrue(totalFailures + " failures Occurred", totalFailures == 0);
+    }
+
+    private class ReloadThread extends Thread
+    {
+        CombinedConfiguration combined;
+        int[] failures;
+        int index;
+        int count;
+        String expected;
+        String id;
+        boolean useId;
+
+        ReloadThread(CombinedConfiguration config, int[] failures, int index, int count,
+                     boolean useId, String id, String expected)
+        {
+            combined = config;
+            this.failures = failures;
+            this.index = index;
+            this.count = count;
+            this.expected = expected;
+            this.id = id;
+            this.useId = useId;
+        }
+        public void run()
+        {
+            failures[index] = 0;
+
+            if (useId)
+            {
+                ThreadLookup.setId(id);
+            }
+            for (int i = 0; i < count; i++)
+            {
+                try
+                {
+                    String value = combined.getString("rowsPerPage", null);
+                    if (value == null || !value.equals(expected))
+                    {
+                        ++failures[index];
+                    }
+                }
+                catch (Exception ex)
+                {
+                    ++failures[index];
+                }
+            }
+        }
+    }
+
     private void verify(String key, DynamicCombinedConfiguration config, int rows)
     {
         System.setProperty("Id", key);
         assertTrue(config.getInt("rowsPerPage") == rows);
     }
+
+    public static class ThreadLookup extends StrLookup
+    {
+        private static ThreadLocal id = new ThreadLocal();
+
+
+
+        public ThreadLookup()
+        {
+
+        }
+
+        public static void setId(String value)
+        {
+            id.set(value);
+        }
+
+        public String lookup(String key)
+        {
+            if (key == null || !key.equals("Id"))
+            {
+                return null;
+            }
+            String value = System.getProperty("Id");
+            if (value != null)
+            {
+                return value;
+            }
+            return (String)id.get();
+
+        }
+    }
 }

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestXMLConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestXMLConfiguration.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestXMLConfiguration.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/TestXMLConfiguration.java Tue Sep 29 23:38:52 2009
@@ -639,6 +639,39 @@
         }
     }
 
+    public void testReloadingOOM() throws Exception
+    {
+        assertNotNull(conf.getReloadingStrategy());
+        assertTrue(conf.getReloadingStrategy() instanceof InvariantReloadingStrategy);
+        PrintWriter out = null;
+
+        try
+        {
+            out = new PrintWriter(new FileWriter(testSaveConf));
+            out.println("<?xml version=\"1.0\"?><config><test>1</test></config>");
+            out.close();
+            out = null;
+            conf.setFile(testSaveConf);
+            FileAlwaysReloadingStrategy strategy = new FileAlwaysReloadingStrategy();
+            strategy.setRefreshDelay(100);
+            conf.setReloadingStrategy(strategy);
+            conf.load();
+            assertEquals(1, conf.getInt("test"));
+
+            for (int i = 1; i < 50000; ++i)
+            {
+               assertEquals(1, conf.getInt("test"));
+            }
+        }
+        finally
+        {
+            if (out != null)
+            {
+                out.close();
+            }
+        }
+    }
+
     /**
      * Tests access to tag names with delimiter characters.
      */
@@ -1615,7 +1648,7 @@
 
         for (int i = 0; i < 2000; i++)
         {
-            assertTrue("Property not found", config.getProperty("test.short") != null); 
+            assertTrue("Property not found", config.getProperty("test.short") != null);
         }
 
         for (int i = 0; i < testThreads.length; ++i)

Modified: commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/reloading/FileRandomReloadingStrategy.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/reloading/FileRandomReloadingStrategy.java?rev=820124&r1=820123&r2=820124&view=diff
==============================================================================
--- commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/reloading/FileRandomReloadingStrategy.java (original)
+++ commons/proper/configuration/branches/CONFIGURATION_390/src/test/org/apache/commons/configuration/reloading/FileRandomReloadingStrategy.java Tue Sep 29 23:38:52 2009
@@ -1,5 +1,8 @@
 package org.apache.commons.configuration.reloading;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import java.io.File;
 import java.util.Random;
 
@@ -9,6 +12,10 @@
 public class FileRandomReloadingStrategy extends FileChangedReloadingStrategy
 {
     Random random = new Random();
+
+      /** The Log to use for diagnostic messages */
+    private Log logger = LogFactory.getLog(FileRandomReloadingStrategy.class);
+
     /**
      * Checks whether a reload is necessary.
      *
@@ -16,7 +23,15 @@
      */
     public boolean reloadingRequired()
     {
-        return random.nextBoolean();
+        boolean result = random.nextBoolean();
+        if (result)
+        {
+            if (logger.isDebugEnabled())
+            {
+                logger.debug("File change detected: " + getName());
+            }
+        }
+        return result;
     }
 
     /**
@@ -28,4 +43,27 @@
     {
         return getFile();
     }
+
+    private String getName()
+    {
+        return getName(getFile());
+    }
+
+    private String getName(File file)
+    {
+        String name = configuration.getURL().toString();
+        if (name == null)
+        {
+            if (file != null)
+            {
+                name = file.getAbsolutePath();
+            }
+            else
+            {
+                name = "base: " + configuration.getBasePath()
+                       + "file: " + configuration.getFileName();
+            }
+        }
+        return name;
+    }
 }