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]