Author: oheger
Date: Thu Jun 21 12:57:25 2007
New Revision: 549591

URL: http://svn.apache.org/viewvc?view=rev&rev=549591
Log:
CONFIGURATION-281: Cycles in the JNDI tree no longer cause a stack overflow in 
JNDIConfiguration

Modified:
    
jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/JNDIConfiguration.java
    
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/MockInitialContextFactory.java
    
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestJNDIConfiguration.java
    jakarta/commons/proper/configuration/trunk/xdocs/changes.xml

Modified: 
jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/JNDIConfiguration.java
URL: 
http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/JNDIConfiguration.java?view=diff&rev=549591&r1=549590&r2=549591
==============================================================================
--- 
jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/JNDIConfiguration.java
 (original)
+++ 
jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/JNDIConfiguration.java
 Thu Jun 21 12:57:25 2007
@@ -115,10 +115,12 @@
      * @param keys All the keys that have been found.
      * @param context The parent context
      * @param prefix What prefix we are building on.
+     * @param processedCtx a set with the so far processed objects
      * @throws NamingException If JNDI has an issue.
      */
-    private void recursiveGetKeys(Set keys, Context context, String prefix) 
throws NamingException
+    private void recursiveGetKeys(Set keys, Context context, String prefix, 
Set processedCtx) throws NamingException
     {
+        processedCtx.add(context);
         NamingEnumeration elements = null;
 
         try
@@ -145,7 +147,11 @@
                 {
                     // add the keys of the sub context
                     Context subcontext = (Context) object;
-                    recursiveGetKeys(keys, subcontext, key.toString());
+                    if (!processedCtx.contains(subcontext))
+                    {
+                        recursiveGetKeys(keys, subcontext, key.toString(),
+                                processedCtx);
+                    }
                 }
                 else
                 {
@@ -202,7 +208,7 @@
             Set keys = new HashSet();
             if (context != null)
             {
-                recursiveGetKeys(keys, context, prefix);
+                recursiveGetKeys(keys, context, prefix, new HashSet());
             }
             else if (containsKey(prefix))
             {

Modified: 
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/MockInitialContextFactory.java
URL: 
http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/MockInitialContextFactory.java?view=diff&rev=549591&r1=549590&r2=549591
==============================================================================
--- 
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/MockInitialContextFactory.java
 (original)
+++ 
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/MockInitialContextFactory.java
 Thu Jun 21 12:57:25 2007
@@ -39,12 +39,21 @@
  */
 public class MockInitialContextFactory implements InitialContextFactory
 {
+    /**
+     * Constant for the use cycles environment property. If this property is
+     * present in the environment, a cyclic context will be created.
+     */
+    public static final String PROP_CYCLES = "useCycles";
+
     /** Constant for the lookup method. */
     private static final String METHOD_LOOKUP = "lookup";
 
     /** Constant for the list method. */
     private static final String METHOD_LIST = "list";
 
+    /** Constant for the close method.*/
+    private static final String METHOD_CLOSE = "close";
+
     /** Constant for the name of the missing property. */
     private static final String MISSING_PROP = "/missing";
 
@@ -75,7 +84,10 @@
      */
     public Context getInitialContext(Hashtable env) throws NamingException
     {
+        boolean useCycles = env.containsKey(PROP_CYCLES);
+
         Mock mockTopCtx = createCtxMock(PREFIX);
+        Mock mockCycleCtx = createCtxMock("");
         Mock mockPrfxCtx = createCtxMock("");
         Mock mockBaseCtx = new Mock(Context.class);
         mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq(""), 
mockTopCtx.proxy());
@@ -83,12 +95,33 @@
                 .proxy());
         mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx
                 .proxy());
-        mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
-                mockTopCtx, new String[]
-                { "test" }, new Object[]
-                { mockPrfxCtx.proxy() }).proxy());
         mockPrfxCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
                 mockPrfxCtx, PROP_NAMES, PROP_VALUES).proxy());
+
+        if (useCycles)
+        {
+            mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycle"),
+                    mockCycleCtx.proxy());
+            mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
+                    mockTopCtx, new String[]
+                    { "test", "cycle" }, new Object[]
+                    { mockPrfxCtx.proxy(), mockCycleCtx.proxy() }).proxy());
+            Mock mockEnum = createEnumMock(mockCycleCtx, PROP_NAMES,
+                    PROP_VALUES, false);
+            addEnumPair(mockEnum, "cycleCtx", mockCycleCtx.proxy());
+            closeEnum(mockEnum);
+            mockCycleCtx
+                    .matchAndReturn(METHOD_LIST, C.eq(""), mockEnum.proxy());
+            mockCycleCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycleCtx"),
+                    mockCycleCtx.proxy());
+        }
+        else
+        {
+            mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
+                    mockTopCtx, new String[]
+                    { "test" }, new Object[]
+                    { mockPrfxCtx.proxy() }).proxy());
+        }
         return (Context) mockBaseCtx.proxy();
     }
 
@@ -112,6 +145,8 @@
         {
             bindError(mockCtx, MISSING_NAMES[i]);
         }
+        mockCtx.matchAndReturn("hashCode", 
System.identityHashCode(mockCtx.proxy()));
+        
         return mockCtx;
     }
 
@@ -146,20 +181,61 @@
      * @param mockCtx the mock representing the context
      * @param names the names contained in the iteration
      * @param values the corresponding values
+     * @param close a flag whether the enumeration should expect to be closed
      * @return the mock for the enumeration
      */
-    private Mock createEnumMock(Mock mockCtx, String[] names, Object[] values)
+    private Mock createEnumMock(Mock mockCtx, String[] names, Object[] values,
+            boolean close)
     {
         Mock mockEnum = new Mock(NamingEnumeration.class);
         for (int i = 0; i < names.length; i++)
         {
-            NameClassPair ncp = new NameClassPair(names[i], values[i]
-                    .getClass().getName());
-            mockEnum.expectAndReturn("hasMore", true);
-            mockEnum.expectAndReturn("next", ncp);
+            addEnumPair(mockEnum, names[i], values[i]);
+        }
+        if (close)
+        {
+            closeEnum(mockEnum);
         }
-        mockEnum.expectAndReturn("hasMore", false);
-        mockEnum.expect("close");
         return mockEnum;
+    }
+
+    /**
+     * Creates and initializes a mock for a naming enumeration that expects to
+     * be closed. This is a shortcut of createEnumMock(mockCtx, names, values,
+     * true);
+     *
+     * @param mockCtx the mock representing the context
+     * @param names the names contained in the iteration
+     * @param values the corresponding values
+     * @return the mock for the enumeration
+     */
+    private Mock createEnumMock(Mock mockCtx, String[] names, Object[] values)
+    {
+        return createEnumMock(mockCtx, names, values, true);
+    }
+
+    /**
+     * Adds a new name-and-value pair to an enum mock.
+     *
+     * @param mockEnum the enum mock
+     * @param name the name
+     * @param value the value
+     */
+    private void addEnumPair(Mock mockEnum, String name, Object value)
+    {
+        NameClassPair ncp = new NameClassPair(name, 
value.getClass().getName());
+        mockEnum.expectAndReturn("hasMore", true);
+        mockEnum.expectAndReturn("next", ncp);
+    }
+
+    /**
+     * Closes an enumeration mock.
+     *
+     * @param mockEnum the mock
+     */
+    private void closeEnum(Mock mockEnum)
+    {
+        mockEnum.expectAndReturn("hasMore", false);
+        mockEnum.expect(METHOD_CLOSE);
     }
 }

Modified: 
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestJNDIConfiguration.java
URL: 
http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestJNDIConfiguration.java?view=diff&rev=549591&r1=549590&r2=549591
==============================================================================
--- 
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestJNDIConfiguration.java
 (original)
+++ 
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestJNDIConfiguration.java
 Thu Jun 21 12:57:25 2007
@@ -17,6 +17,8 @@
 
 package org.apache.commons.configuration;
 
+import java.util.Hashtable;
+
 import junit.framework.TestCase;
 
 import javax.naming.Context;
@@ -259,6 +261,18 @@
     }
 
     /**
+     * Tests the getKeys() method when there are cycles in the tree.
+     */
+    public void testGetKeysWithCycles() throws NamingException
+    {
+        Hashtable env = new Hashtable();
+        env.put(MockInitialContextFactory.PROP_CYCLES, Boolean.TRUE);
+        InitialContext initCtx = new InitialContext(env);
+        conf = new JNDIConfiguration(initCtx);
+        conf.getKeys("cycle");
+    }
+
+    /**
      * A special JNDI configuration implementation that can be configured to
      * throw an exception when accessing the base context. Used for testing the
      * exception handling.
@@ -271,6 +285,11 @@
         public PotentialErrorJNDIConfiguration() throws NamingException
         {
             super();
+        }
+
+        public PotentialErrorJNDIConfiguration(Context ctx) throws 
NamingException
+        {
+            super(ctx);
         }
 
         public Context getBaseContext() throws NamingException

Modified: jakarta/commons/proper/configuration/trunk/xdocs/changes.xml
URL: 
http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/xdocs/changes.xml?view=diff&rev=549591&r1=549590&r2=549591
==============================================================================
--- jakarta/commons/proper/configuration/trunk/xdocs/changes.xml (original)
+++ jakarta/commons/proper/configuration/trunk/xdocs/changes.xml Thu Jun 21 
12:57:25 2007
@@ -23,6 +23,10 @@
 
   <body>
     <release version="1.5-SNAPSHOT" date="in SVN" description="">
+      <action dev="oheger" type="fix" issue="CONFIGURATION-281">
+        Cycles in the JNDI tree no longer cause a stack overflow in
+        JNDIConfiguration.
+      </action>
       <action dev="oheger" type="add" issue="CONFIGURATION-277">
         The base implementation of clear() in AbstractConfiguration now checks
         for a potential UnsupportedOperationException when iterating over the



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to