Author: ebourg
Date: Sun Apr 13 16:59:38 2008
New Revision: 647641
URL: http://svn.apache.org/viewvc?rev=647641&view=rev
Log:
Reimplemented JNDIConfiguration to make it hierarchical and support write
operations
Merged the two JNDI test cases
Removed:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestJNDIEnvironmentValues.java
Modified:
commons/proper/configuration/branches/configuration2_experimental/pom.xml
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/JNDIConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestJNDIConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml
Modified:
commons/proper/configuration/branches/configuration2_experimental/pom.xml
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/pom.xml?rev=647641&r1=647640&r2=647641&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/pom.xml
(original)
+++ commons/proper/configuration/branches/configuration2_experimental/pom.xml
Sun Apr 13 16:59:38 2008
@@ -305,6 +305,13 @@
<version>2.2</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>spice</groupId>
+ <artifactId>spice-jndikit</artifactId>
+ <version>1.2</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java?rev=647641&r1=647640&r2=647641&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
Sun Apr 13 16:59:38 2008
@@ -633,9 +633,7 @@
{
visitor.visitBeforeChildren(node, getNodeHandler());
- for (Iterator<T> it =
getNodeHandler().getChildren(node).iterator(); it
- .hasNext()
- && !visitor.terminate();)
+ for (Iterator<T> it =
getNodeHandler().getChildren(node).iterator(); it.hasNext() &&
!visitor.terminate();)
{
visit(it.next(), visitor);
}
@@ -876,13 +874,15 @@
* @param parentKey the key of the parent node
* @param handler the node handler
*/
- public void appendAttributes(T node, String parentKey,
- NodeHandler<T> handler)
+ public void appendAttributes(T node, String parentKey, NodeHandler<T>
handler)
{
- for (String attr : handler.getAttributes(node))
+ List<String> attributes = handler.getAttributes(node);
+ if (attributes != null)
{
- keyList.add(getExpressionEngine().attributeKey(node, parentKey,
- attr, handler));
+ for (String attr : attributes)
+ {
+ keyList.add(getExpressionEngine().attributeKey(node,
parentKey, attr, handler));
+ }
}
}
}
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/JNDIConfiguration.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/JNDIConfiguration.java?rev=647641&r1=647640&r2=647641&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/JNDIConfiguration.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/JNDIConfiguration.java
Sun Apr 13 16:59:38 2008
@@ -18,11 +18,7 @@
package org.apache.commons.configuration2;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
@@ -30,32 +26,38 @@
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
-import javax.naming.NotContextException;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.configuration2.expr.AbstractNodeHandler;
/**
* This Configuration class allows you to interface with a JNDI datasource.
- * A JNDIConfiguration is read-only, write operations will throw an
- * UnsupportedOperationException. The clear operations are supported but the
- * underlying JNDI data source is not changed.
+ * Unlike other configurations it's not possible to set a property to a path
+ * that's the prefix of another property. For example the following properties
+ * couldn't be stored simultaneously in a JNDIConfiguration:
+ *
+ * <pre>
+ * test.foo = value1
+ * test.foo.bar = value2
+ * </pre>
+ *
+ * <p>In this case setting the <tt>test.foo.bar</tt> property will overwrite
+ * <tt>test.foo</tt>, and reciprocally.</p>
+ *
+ * <p>A maximum depth is assigned to the configuration, it is set to 20
+ * by default. Since JNDI directories can have cyclic paths, this depth
+ * prevents infinite loops when searching through the tree.</p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Eric Pugh</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Emmanuel Bourg</a>
* @version $Id$
*/
-public class JNDIConfiguration extends AbstractConfiguration
+public class JNDIConfiguration extends
AbstractHierarchicalConfiguration<JNDIConfiguration.JNDINode>
{
- /** The prefix of the context. */
- private String prefix;
-
- /** The initial JNDI context. */
- private Context context;
-
- /** The base JNDI context. */
- private Context baseContext;
+ /** The root node of the configuration. */
+ private JNDINode root;
- /** The Set of keys that have been virtually cleared. */
- private Set<String> clearedProperties = new HashSet<String>();
+ /** The maximum depth for fetching the child nodes in the JNDI tree. */
+ private int maxDepth = 20;
/**
* Creates a JNDIConfiguration using the default initial context as the
@@ -65,7 +67,7 @@
*/
public JNDIConfiguration() throws NamingException
{
- this((String) null);
+ this("");
}
/**
@@ -89,7 +91,7 @@
*/
public JNDIConfiguration(Context context)
{
- this(context, null);
+ this(context, "");
}
/**
@@ -101,369 +103,309 @@
*/
public JNDIConfiguration(Context context, String prefix)
{
- this.context = context;
- this.prefix = prefix;
+ super(new JNDINodeHandler());
+ ((JNDINodeHandler) getNodeHandler()).setConfiguration(this);
+
+ root = new JNDINode(context, prefix, 0);
+
setLogger(Logger.getLogger(getClass().getName()));
addErrorLogListener();
}
+ public JNDINode getRootNode()
+ {
+ return root;
+ }
+
/**
- * This method recursive traverse the JNDI tree, looking for Context
objects.
- * When it finds them, it traverses them as well. Otherwise it just adds
the
- * values to the list of keys found.
- *
- * @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.
+ * Returns the maximum depth for searching in the JNDI tree.
*/
- private void recursiveGetKeys(Set<String> keys, Context context, String
prefix, Set<Context> processedCtx) throws NamingException
+ public int getMaxDepth()
{
- processedCtx.add(context);
- NamingEnumeration elements = null;
+ return maxDepth;
+ }
- try
- {
- elements = context.list("");
+ /**
+ * Sets the maximum depth for searching in the JNDI tree.
+ *
+ * @param maxDepth the maximum depth
+ */
+ public void setMaxDepth(int maxDepth)
+ {
+ this.maxDepth = maxDepth;
+ }
- // iterates through the context's elements
- while (elements.hasMore())
- {
- NameClassPair nameClassPair = (NameClassPair) elements.next();
- String name = nameClassPair.getName();
- Object object = context.lookup(name);
+ /**
+ * Returns the prefix.
+ *
+ * @return the prefix
+ */
+ public String getPrefix()
+ {
+ return root.name;
+ }
- // build the key
- StringBuilder key = new StringBuilder();
- key.append(prefix);
- if (key.length() > 0)
- {
- key.append(".");
- }
- key.append(name);
+ /**
+ * Sets the prefix.
+ *
+ * @param prefix The prefix to set
+ */
+ public void setPrefix(String prefix)
+ {
+ root = new JNDINode(root.context, prefix, 0);
+ }
- if (object instanceof Context)
- {
- // add the keys of the sub context
- Context subcontext = (Context) object;
- if (!processedCtx.contains(subcontext))
- {
- recursiveGetKeys(keys, subcontext, key.toString(),
- processedCtx);
- }
- }
- else
- {
- // add the key
- keys.add(key.toString());
- }
- }
- }
- finally
- {
- // close the enumeration
- if (elements != null)
- {
- elements.close();
- }
- }
+ /**
+ * Return the base context with the prefix applied.
+ *
+ * @return the base context
+ * @throws NamingException if an error occurs
+ */
+ public Context getBaseContext() throws NamingException
+ {
+ return (Context) root.context.lookup(root.name);
}
/**
- * Returns an iterator with all property keys stored in this configuration.
+ * Return the initial context used by this configuration. This context is
+ * independent of the prefix specified.
*
- * @return an iterator with all keys
+ * @return the initial context
*/
- public Iterator<String> getKeys()
+ public Context getContext()
{
- return getKeys("");
+ return root.context;
}
/**
- * Returns an iterator with all property keys starting with the given
- * prefix.
+ * Set the initial context of the configuration.
*
- * @param prefix the prefix
- * @return an iterator with the selected keys
+ * @param context the context
*/
- public Iterator<String> getKeys(String prefix)
+ public void setContext(Context context)
{
- // build the path
- List<String> path = Arrays.asList(StringUtils.split(prefix, "."));
+ root = new JNDINode(context, root.name, 0);
+ }
+
+ /**
+ * Node of a JNDI directory. A node consists in a base context and a name
+ * of a property bound. An empty name refers to the context itself.
+ */
+ static class JNDINode {
+ private Context context;
+ private String name;
+ private int depth;
- try
+ private JNDINode(Context context, String name, int depth)
{
- // find the context matching the specified path
- Context context = getContext(path, getBaseContext());
+ this.context = context;
+ this.name = name;
+ this.depth = depth;
+ }
- // return all the keys under the context found
- Set<String> keys = new HashSet<String>();
- if (context != null)
+ public Object getValue() throws NamingException
+ {
+ try
{
- recursiveGetKeys(keys, context, prefix, new
HashSet<Context>());
+ return context.lookup(name);
}
- else if (containsKey(prefix))
+ catch (NameNotFoundException e)
{
- // add the prefix if it matches exactly a property key
- keys.add(prefix);
+ return null;
}
-
- return keys.iterator();
- }
- catch (NamingException e)
- {
- fireError(EVENT_READ_PROPERTY, null, null, e);
- return new ArrayList<String>().iterator();
}
}
/**
- * Because JNDI is based on a tree configuration, we need to filter down
the
- * tree, till we find the Context specified by the key to start from.
- * Otherwise return null.
- *
- * @param path the path of keys to traverse in order to find the
context
- * @param context the context to start from
- * @return The context at that key's location in the JNDI tree, or null if
not found
- * @throws NamingException if JNDI has an issue
+ * Implementation of NodeHandler that operates on JNDI trees.
*/
- private Context getContext(List path, Context context) throws
NamingException
+ private static class JNDINodeHandler extends AbstractNodeHandler<JNDINode>
{
- // return the current context if the path is empty
- if (path == null || path.isEmpty())
+ private JNDIConfiguration config;
+
+ public void setConfiguration(JNDIConfiguration config)
{
- return context;
+ this.config = config;
}
- String key = (String) path.get(0);
+ public boolean hasAttributes(JNDINode node)
+ {
+ return false;
+ }
- // search a context matching the key in the context's elements
- NamingEnumeration elements = null;
+ public String nodeName(JNDINode node)
+ {
+ return node.name;
+ }
- try
+ public Object getValue(JNDINode node)
{
- elements = context.list("");
- while (elements.hasMore())
+ try
{
- NameClassPair nameClassPair = (NameClassPair) elements.next();
- String name = nameClassPair.getName();
- Object object = context.lookup(name);
-
- if (object instanceof Context && name.equals(key))
+ Object value = node.getValue();
+ if (value instanceof Context)
{
- Context subcontext = (Context) object;
-
- // recursive search in the sub context
- return getContext(path.subList(1, path.size()),
subcontext);
+ // contexts have no direct value bound
+ return null;
+ }
+ else
+ {
+ return value;
}
}
- }
- finally
- {
- if (elements != null)
+ catch (NamingException e)
{
- elements.close();
+ throw new ConfigurationRuntimeException("Unable to get the
value of the JNDI node", e);
}
}
- return null;
- }
-
- /**
- * Returns a flag whether this configuration is empty.
- *
- * @return the empty flag
- */
- public boolean isEmpty()
- {
- try
+ public void setValue(JNDINode node, Object value)
{
- NamingEnumeration enumeration = null;
-
try
{
- enumeration = getBaseContext().list("");
- return !enumeration.hasMore();
- }
- finally
- {
- // close the enumeration
- if (enumeration != null)
+ if (value == null)
+ {
+ node.context.unbind(node.name);
+ }
+ else
{
- enumeration.close();
+ node.context.rebind(node.name, value);
}
}
+ catch (NamingException e)
+ {
+ throw new ConfigurationRuntimeException("Unable to set the
value of the JNDI node", e);
+ }
}
- catch (NamingException e)
+
+ public JNDINode getParent(JNDINode node)
{
- fireError(EVENT_READ_PROPERTY, null, null, e);
- return true;
+ return null; // todo
}
- }
- /**
- * <p><strong>This operation is not supported and will throw an
- * UnsupportedOperationException.</strong></p>
- *
- * @param key the key
- * @param value the value
- * @throws UnsupportedOperationException
- */
- public void setProperty(String key, Object value)
- {
- throw new UnsupportedOperationException("This operation is not
supported");
- }
+ public JNDINode addChild(JNDINode node, String name)
+ {
+ try
+ {
+ Object value = node.getValue();
- /**
- * Removes the specified property.
- *
- * @param key the key of the property to remove
- */
- public void clearProperty(String key)
- {
- clearedProperties.add(key);
- }
+ if (!(value instanceof Context))
+ {
+ // overwrite the existing property at this path
+ node.context.unbind(node.name);
- /**
- * Checks whether the specified key is contained in this configuration.
- *
- * @param key the key to check
- * @return a flag whether this key is stored in this configuration
- */
- public boolean containsKey(String key)
- {
- if (clearedProperties.contains(key))
- {
- return false;
- }
- key = StringUtils.replace(key, ".", "/");
- try
- {
- // throws a NamingException if JNDI doesn't contain the key.
- getBaseContext().lookup(key);
- return true;
- }
- catch (NameNotFoundException e)
- {
- // expected exception, no need to log it
- return false;
+ value = node.context.createSubcontext(node.name);
+ }
+
+ Context context = (Context) value;
+ context.createSubcontext(name);
+
+ return new JNDINode(context, name, node.depth + 1);
+ }
+ catch (NamingException e)
+ {
+ throw new ConfigurationRuntimeException("Unable to add the
child node '" + name + "'", e);
+ }
}
- catch (NamingException e)
+
+ public List<JNDINode> getChildren(JNDINode node)
{
- fireError(EVENT_READ_PROPERTY, key, null, e);
- return false;
+ List<JNDINode> children = new ArrayList<JNDINode>();
+
+ try
+ {
+ Object value = node.getValue();
+ if (value instanceof Context && node.depth <=
config.getMaxDepth())
+ {
+ Context context = (Context) value;
+
+ NamingEnumeration elements = null;
+
+ try
+ {
+ elements = context.list("");
+ while (elements.hasMore())
+ {
+ NameClassPair nameClassPair = (NameClassPair)
elements.next();
+ String name = nameClassPair.getName();
+
+ children.add(new JNDINode(context, name,
node.depth + 1));
+ }
+ }
+ finally
+ {
+ if (elements != null)
+ {
+ elements.close();
+ }
+ }
+ }
+ }
+ catch (NamingException e)
+ {
+ config.fireError(EVENT_READ_PROPERTY, null, null, e);
+ }
+
+ return children;
}
- }
- /**
- * Returns the prefix.
- * @return the prefix
- */
- public String getPrefix()
- {
- return prefix;
- }
+ public List<JNDINode> getChildren(JNDINode node, String name)
+ {
+ List<JNDINode> nodes = new ArrayList<JNDINode>(1);
- /**
- * Sets the prefix.
- *
- * @param prefix The prefix to set
- */
- public void setPrefix(String prefix)
- {
- this.prefix = prefix;
+ for (JNDINode n : getChildren(node))
+ {
+ if (name.equals(n.name)) {
+ nodes.add(n);
+ break;
+ }
+ }
- // clear the previous baseContext
- baseContext = null;
- }
+ return nodes;
+ }
- /**
- * Returns the value of the specified property.
- *
- * @param key the key of the property
- * @return the value of this property
- */
- public Object getProperty(String key)
- {
- if (clearedProperties.contains(key))
+ public JNDINode getChild(JNDINode node, int index)
{
- return null;
+ return getChildren(node).get(index);
}
- try
+ public int getChildrenCount(JNDINode node, String name)
{
- key = StringUtils.replace(key, ".", "/");
- return getBaseContext().lookup(key);
+ return 1;
}
- catch (NameNotFoundException e)
+
+ public void removeChild(JNDINode node, JNDINode child)
{
- // expected exception, no need to log it
- return null;
+ try
+ {
+ child.context.unbind(child.name);
+ }
+ catch (NamingException e)
+ {
+ throw new ConfigurationRuntimeException("Unable to remove the
child JNDI node", e);
+ }
}
- catch (NotContextException nctxex)
+
+ public List<String> getAttributes(JNDINode node)
{
- // expected exception, no need to log it
return null;
}
- catch (NamingException e)
+
+ public Object getAttributeValue(JNDINode node, String name)
{
- fireError(EVENT_READ_PROPERTY, key, null, e);
return null;
}
- }
-
- /**
- * <p><strong>This operation is not supported and will throw an
- * UnsupportedOperationException.</strong></p>
- *
- * @param key the key
- * @param obj the value
- * @throws UnsupportedOperationException
- */
- protected void addPropertyDirect(String key, Object obj)
- {
- throw new UnsupportedOperationException("This operation is not
supported");
- }
- /**
- * Return the base context with the prefix applied.
- *
- * @return the base context
- * @throws NamingException if an error occurs
- */
- public Context getBaseContext() throws NamingException
- {
- if (baseContext == null)
+ public void setAttributeValue(JNDINode node, String name, Object value)
{
- baseContext = (Context) getContext().lookup(prefix == null ? "" :
prefix);
}
- return baseContext;
- }
-
- /**
- * Return the initial context used by this configuration. This context is
- * independent of the prefix specified.
- *
- * @return the initial context
- */
- public Context getContext()
- {
- return context;
- }
-
- /**
- * Set the initial context of the configuration.
- *
- * @param context the context
- */
- public void setContext(Context context)
- {
- // forget the removed properties
- clearedProperties.clear();
+ public void addAttributeValue(JNDINode node, String name, Object value)
+ {
+ }
- // change the context
- this.context = context;
+ public void removeAttribute(JNDINode node, String name)
+ {
+ }
}
}
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java?rev=647641&r1=647640&r2=647641&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/MockInitialContextFactory.java
Sun Apr 13 16:59:38 2008
@@ -14,27 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.commons.configuration2;
import java.util.Hashtable;
-
import javax.naming.Context;
-import javax.naming.NameClassPair;
-import javax.naming.NameNotFoundException;
-import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
-import com.mockobjects.dynamic.C;
-import com.mockobjects.dynamic.Mock;
+import org.codehaus.spice.jndikit.DefaultNameParser;
+import org.codehaus.spice.jndikit.DefaultNamespace;
+import org.codehaus.spice.jndikit.memory.MemoryContext;
/**
* A mock implementation of the <code>InitialContextFactory</code> interface.
* This implementation will return a mock context that contains some test data.
*
- * @author <a
- * href="http://commons.apache.org/configuration/team-list.html">Commons
- * Configuration team</a>
+ * @author <a
href="http://commons.apache.org/configuration/team-list.html">Commons
Configuration team</a>
* @version $Id$
*/
public class MockInitialContextFactory implements InitialContextFactory
@@ -45,197 +41,29 @@
*/
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";
-
- /** Constant for the name of the prefix. */
- private static final String PREFIX = "test/";
-
- /** An array with the names of the supported properties. */
- private static final String[] PROP_NAMES =
- { "key", "key2", "short", "boolean", "byte", "double", "float", "integer",
- "long", "onlyinjndi" };
-
- /** An array with the values of the supported properties. */
- private static final String[] PROP_VALUES =
- { "jndivalue", "jndivalue2", "1", "true", "10", "10.25", "20.25", "10",
- "1000000", "true" };
-
- /** An array with properties that are requested, but are not in the
context. */
- private static final String[] MISSING_NAMES =
- { "missing/list", "test/imaginarykey", "foo/bar" };
-
- /**
- * Creates a <code>Context</code> object that is backed by a mock object.
- * The mock context can be queried for the values of certain test
- * properties. It also supports listing the contained (sub) properties.
- *
- * @param env the environment
- * @return the context mock
- */
public Context getInitialContext(Hashtable env) throws NamingException
{
- boolean useCycles = env.containsKey(PROP_CYCLES);
+ DefaultNamespace namespace = new DefaultNamespace(new
DefaultNameParser());
+ MemoryContext context = new MemoryContext(namespace, new Hashtable(),
null);
- Mock mockTopCtx = createCtxMock(PREFIX);
- Mock mockCycleCtx = createCtxMock("");
- Mock mockPrfxCtx = createCtxMock("");
- Mock mockBaseCtx = new Mock(Context.class);
- mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq(""),
mockTopCtx.proxy());
- mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx
- .proxy());
- mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx
- .proxy());
- mockPrfxCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
- mockPrfxCtx, PROP_NAMES, PROP_VALUES).proxy());
+ Context testContext = context.createSubcontext("test");
+ testContext.bind("key", "jndivalue");
+ testContext.bind("key2","jndivalue2");
+ testContext.bind("short","1");
+ testContext.bind("boolean","true");
+ testContext.bind("byte","10");
+ testContext.bind("double","10.25");
+ testContext.bind("float","20.25");
+ testContext.bind("integer","10");
+ testContext.bind("long","1000000");
+ testContext.bind("onlyinjndi","true");
- 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
+ if (env.containsKey(PROP_CYCLES))
{
- mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
- mockTopCtx, new String[]
- { "test" }, new Object[]
- { mockPrfxCtx.proxy() }).proxy());
+ Context cycleContext = context.createSubcontext("cycle");
+ cycleContext.bind("cycle", cycleContext);
}
- return (Context) mockBaseCtx.proxy();
- }
- /**
- * Creates a mock for a Context with the specified prefix.
- *
- * @param prefix the prefix
- * @return the mock for the context
- */
- private Mock createCtxMock(String prefix)
- {
- Mock mockCtx = new Mock(Context.class);
- for (int i = 0; i < PROP_NAMES.length; i++)
- {
- bind(mockCtx, prefix + PROP_NAMES[i], PROP_VALUES[i]);
- String errProp = (prefix.length() > 0) ? PROP_NAMES[i] : PREFIX
- + PROP_NAMES[i];
- bindError(mockCtx, errProp);
- }
- for (int i = 0; i < MISSING_NAMES.length; i++)
- {
- bindError(mockCtx, MISSING_NAMES[i]);
- }
- mockCtx.matchAndReturn("hashCode",
System.identityHashCode(mockCtx.proxy()));
-
- return mockCtx;
- }
-
- /**
- * Binds a property value to the mock context.
- *
- * @param mockCtx the context
- * @param name the name of the property
- * @param value the value of the property
- */
- private void bind(Mock mockCtx, String name, String value)
- {
- mockCtx.matchAndReturn(METHOD_LOOKUP, C.eq(name), value);
- bindError(mockCtx, name + MISSING_PROP);
- }
-
- /**
- * Configures the mock to expect a call for a non existing property.
- *
- * @param mockCtx the mock
- * @param name the name of the property
- */
- private void bindError(Mock mockCtx, String name)
- {
- mockCtx.matchAndThrow(METHOD_LOOKUP, C.eq(name),
- new NameNotFoundException("unknown property"));
- }
-
- /**
- * Creates and initializes a mock for a naming enumeration.
- *
- * @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,
- boolean close)
- {
- Mock mockEnum = new Mock(NamingEnumeration.class);
- for (int i = 0; i < names.length; i++)
- {
- addEnumPair(mockEnum, names[i], values[i]);
- }
- if (close)
- {
- closeEnum(mockEnum);
- }
- 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);
+ return context;
}
}
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestJNDIConfiguration.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestJNDIConfiguration.java?rev=647641&r1=647640&r2=647641&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestJNDIConfiguration.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestJNDIConfiguration.java
Sun Apr 13 16:59:38 2008
@@ -18,24 +18,20 @@
package org.apache.commons.configuration2;
import java.util.Hashtable;
-
-import javax.naming.Context;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import junit.framework.TestCase;
-import org.apache.commons.configuration2.AbstractConfiguration;
-import org.apache.commons.configuration2.JNDIConfiguration;
-import org.apache.commons.configuration2.event.ConfigurationErrorListener;
-
/**
* Test to see if the JNDIConfiguration works properly.
*
* @version $Id$
*/
-public class TestJNDIConfiguration extends TestCase {
-
+public class TestJNDIConfiguration extends TestCase
+{
public static final String CONTEXT_FACTORY =
MockInitialContextFactory.class.getName();
private JNDIConfiguration conf;
@@ -44,11 +40,11 @@
/** A test error listener for counting internal errors.*/
private ConfigurationErrorListenerImpl listener;
- public void setUp() throws Exception {
-
+ public void setUp() throws Exception
+ {
System.setProperty("java.naming.factory.initial", CONTEXT_FACTORY);
- conf = new PotentialErrorJNDIConfiguration();
+ conf = new JNDIConfiguration();
nonStringTestHolder = new NonStringTestHolder();
nonStringTestHolder.setConfiguration(conf);
@@ -70,67 +66,175 @@
super.tearDown();
}
- public void testBoolean() throws Exception {
+ public void testBoolean() throws Exception
+ {
nonStringTestHolder.testBoolean();
}
- public void testBooleanDefaultValue() throws Exception {
+ public void testBooleanDefaultValue() throws Exception
+ {
nonStringTestHolder.testBooleanDefaultValue();
}
- public void testByte() throws Exception {
+ public void testByte() throws Exception
+ {
nonStringTestHolder.testByte();
}
- public void testDouble() throws Exception {
+ public void testDouble() throws Exception
+ {
nonStringTestHolder.testDouble();
}
- public void testDoubleDefaultValue() throws Exception {
+ public void testDoubleDefaultValue() throws Exception
+ {
nonStringTestHolder.testDoubleDefaultValue();
}
- public void testFloat() throws Exception {
+ public void testFloat() throws Exception
+ {
nonStringTestHolder.testFloat();
}
- public void testFloatDefaultValue() throws Exception {
+ public void testFloatDefaultValue() throws Exception
+ {
nonStringTestHolder.testFloatDefaultValue();
}
- public void testInteger() throws Exception {
+ public void testInteger() throws Exception
+ {
nonStringTestHolder.testInteger();
}
- public void testIntegerDefaultValue() throws Exception {
+ public void testIntegerDefaultValue() throws Exception
+ {
nonStringTestHolder.testIntegerDefaultValue();
}
- public void testLong() throws Exception {
+ public void testLong() throws Exception
+ {
nonStringTestHolder.testLong();
}
- public void testLongDefaultValue() throws Exception {
+ public void testLongDefaultValue() throws Exception
+ {
nonStringTestHolder.testLongDefaultValue();
}
- public void testShort() throws Exception {
+ public void testShort() throws Exception
+ {
nonStringTestHolder.testShort();
}
- public void testShortDefaultValue() throws Exception {
+ public void testShortDefaultValue() throws Exception
+ {
nonStringTestHolder.testShortDefaultValue();
}
- public void testListMissing() throws Exception {
+ public void testListMissing() throws Exception
+ {
nonStringTestHolder.testListMissing();
}
- public void testSubset() throws Exception {
+ public void testSubset() throws Exception
+ {
nonStringTestHolder.testSubset();
}
- public void testProperties() throws Exception {
+ public void testSimpleGet() throws Exception
+ {
+ String s = conf.getString("test.key");
+ assertEquals("jndivalue", s);
+ }
+
+ public void testMoreGets() throws Exception
+ {
+ assertEquals("jndivalue", conf.getString("test.key"));
+ assertEquals("jndivalue2", conf.getString("test.key2"));
+ }
+
+ public void testGetMissingKey() throws Exception
+ {
+ try
+ {
+ conf.setThrowExceptionOnMissing(true);
+ conf.getString("test.imaginarykey");
+ fail("Should have thrown NoSuchElementException");
+ }
+ catch (NoSuchElementException e)
+ {
+ assertTrue(e.getMessage(),
e.getMessage().indexOf("test.imaginarykey") != -1);
+ }
+ }
+
+ public void testClearProperty()
+ {
+ assertNotNull("null short for the 'test.short' key",
conf.getShort("test.short", null));
+ conf.clearProperty("test.short");
+ assertNull("'test.short' property not cleared",
conf.getShort("test.short", null));
+ }
+
+ public void testIsEmpty()
+ {
+ assertFalse("the configuration shouldn't be empty", conf.isEmpty());
+
+ conf.clearProperty("test");
+
+ assertTrue("the configuration should be empty", conf.isEmpty());
+ }
+
+ public void testGetKeys() throws Exception
+ {
+ boolean found = false;
+ Iterator it = conf.getKeys();
+
+ assertTrue("no key found", it.hasNext());
+
+ while (it.hasNext() && !found)
+ {
+ found = "test.boolean".equals(it.next());
+ }
+
+ assertTrue("'test.boolean' key not found", found);
+ }
+
+ public void testGetKeysWithUnknownPrefix()
+ {
+ // test for a unknown prefix
+ Iterator it = conf.getKeys("foo.bar");
+ assertFalse("no key should be found", it.hasNext());
+ }
+
+ public void testGetKeysWithExistingPrefix()
+ {
+ // test for an existing prefix
+ Iterator it = conf.getKeys("test");
+ boolean found = false;
+ while (it.hasNext() && !found)
+ {
+ found = "test.boolean".equals(it.next());
+ }
+
+ assertTrue("'test.boolean' key not found", found);
+ }
+
+ public void testGetKeysWithKeyAsPrefix()
+ {
+ // test for a prefix matching exactly the key of a property
+ // todo fails due to CONFIGURATION-321
+ /*
+ Iterator it = conf.getKeys("test.boolean");
+ boolean found = false;
+ while (it.hasNext() && !found)
+ {
+ found = "test.boolean".equals(it.next());
+ }
+
+ assertTrue("'test.boolean' key not found", found);
+ */
+ }
+
+ public void testGetProperty() throws Exception {
Object o = conf.getProperty("test.boolean");
assertNotNull(o);
assertEquals("true", o.toString());
@@ -143,6 +247,39 @@
conf.clearProperty(key);
assertFalse("'" + key + "' still found", conf.containsKey(key));
+
+ assertTrue(conf.containsKey("test.key"));
+ assertFalse(conf.containsKey("test.imaginarykey"));
+ }
+
+ public void testSetProperty()
+ {
+ conf.setProperty("test.new.value", "foo");
+
+ assertEquals("test.new.value", "foo",
conf.getProperty("test.new.value"));
+ }
+
+ public void testReplaceProperty()
+ {
+ conf.setProperty("test.foo", "bar");
+ assertEquals("test.foo", "bar", conf.getProperty("test.foo"));
+
+ conf.setProperty("test.foo", "baz");
+ assertEquals("test.foo", "baz", conf.getProperty("test.foo"));
+ }
+
+ public void testOverwriteProperty()
+ {
+ conf.setProperty("test.foo", "value1");
+ assertEquals("test.foo", "value1", conf.getProperty("test.foo"));
+
+ conf.setProperty("test.foo.bar", "value2");
+ assertEquals("test.foo.bar", "value2",
conf.getProperty("test.foo.bar"));
+ assertEquals("test.foo", null, conf.getProperty("test.foo"));
+
+ conf.setProperty("test.foo", "value1");
+ assertEquals("test.foo.bar", null, conf.getProperty("test.foo.bar"));
+ assertEquals("test.foo", "value1", conf.getProperty("test.foo"));
}
public void testChangePrefix()
@@ -185,82 +322,12 @@
}
/**
- * Configures the test config to throw an exception.
- */
- private PotentialErrorJNDIConfiguration setUpErrorConfig()
- {
- ((PotentialErrorJNDIConfiguration) conf).failOnGetCtx = true;
- conf.removeErrorListener((ConfigurationErrorListener) conf
- .getErrorListeners().iterator().next());
- return (PotentialErrorJNDIConfiguration) conf;
- }
-
- /**
- * Tests whether the expected error events have been received.
- *
- * @param type the expected event type
- * @param propName the name of the property
- * @param propValue the property value
- */
- private void checkErrorListener(int type, String propName, Object
propValue)
- {
- listener.verify(type, propName, propValue);
- assertTrue("Wrong exception class",
- listener.getLastEvent().getCause() instanceof NamingException);
- listener = null;
- }
-
- /**
* Tests whether a JNDI configuration registers an error log listener.
*/
public void testLogListener() throws NamingException
{
conf = new JNDIConfiguration();
- assertEquals("No error log listener registered", 1, conf
- .getErrorListeners().size());
- }
-
- /**
- * Tests handling of errors in getKeys().
- */
- public void testGetKeysError()
- {
- assertFalse("Iteration not empty", setUpErrorConfig().getKeys()
- .hasNext());
- checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null,
- null);
- }
-
- /**
- * Tests handling of errors in isEmpty().
- */
- public void testIsEmptyError() throws NamingException
- {
- assertTrue("Error config not empty", setUpErrorConfig().isEmpty());
- checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null,
- null);
- }
-
- /**
- * Tests handling of errors in the containsKey() method.
- */
- public void testContainsKeyError()
- {
- assertFalse("Key contained after error", setUpErrorConfig()
- .containsKey("key"));
- checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key",
- null);
- }
-
- /**
- * Tests handling of errors in getProperty().
- */
- public void testGetPropertyError()
- {
- assertNull("Wrong property value after error", setUpErrorConfig()
- .getProperty("key"));
- checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key",
- null);
+ assertEquals("No error log listener registered", 1,
conf.getErrorListeners().size());
}
/**
@@ -275,33 +342,12 @@
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.
- */
- static class PotentialErrorJNDIConfiguration extends JNDIConfiguration
+ public void testSetMaxDepth()
{
- /** A flag whether an exception should be thrown. */
- boolean failOnGetCtx;
-
- public PotentialErrorJNDIConfiguration() throws NamingException
- {
- super();
- }
+ conf.setMaxDepth(0);
+ assertFalse("Key found with depth set to 0", conf.getKeys().hasNext());
- public PotentialErrorJNDIConfiguration(Context ctx) throws
NamingException
- {
- super(ctx);
- }
-
- public Context getBaseContext() throws NamingException
- {
- if (failOnGetCtx)
- {
- throw new NamingException("Simulated JNDI exception!");
- }
- return super.getBaseContext();
- }
+ conf.setMaxDepth(1);
+ assertTrue("No key found with depth set to 1",
conf.getKeys().hasNext());
}
-}
\ No newline at end of file
+}
Modified:
commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml?rev=647641&r1=647640&r2=647641&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml
Sun Apr 13 16:59:38 2008
@@ -63,6 +63,9 @@
Their functionality is completely covered by classes in the tree
package.
</action>
+ <action dev="ebourg" type="update">
+ JNDIConfiguration is now a hierarchical configuration and supports
write operations.
+ </action>
</release>
<release version="1.6" date="in SVN" description="">