You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by eb...@apache.org on 2008/04/14 01:59:51 UTC

svn commit: r647641 - in /commons/proper/configuration/branches/configuration2_experimental: ./ src/main/java/org/apache/commons/configuration2/ src/test/java/org/apache/commons/configuration2/ xdocs/

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:epugh@upstate.com">Eric Pugh</a>
+ * @author <a href="mailto:ebourg@apache.org">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="">



Re: svn commit: r647641 - in /commons/proper/configuration/branches/configuration2_experimental: ./ src/main/java/org/apache/commons/configuration2/ src/test/java/org/apache/commons/configuration2/ xdocs/

Posted by Emmanuel Bourg <eb...@apache.org>.
Oliver Heger a écrit :

> - I wonder whether it is possible to implement the JNDINodeHandler so 
> that it directly operates on Context objects. The main reason why I 
> introduced NodeHandlers was that it would no longer be necessary to 
> implement specific node classes. This would also be more efficient in 
> terms of memory usage because there is no need to duplicate the node 
> structure.

It may be possible with the Context/Binding objects, but I don't know 
how to track the depth of the node. I'll give it a try.


> - A minor nit: I would prefer if the node handler returned empty lists 
> instead of null (e.g. for attributes). This simplifies the code in 
> AbstractHierarchicalConfiguration. If the node handler returns 
> Collections.emptyList(), there should not be any performance issues.

Ok, but shouldn't this call be skipped if hasAttributes() returns false ?


> - What exactly is the license of the spice JNDI kit? In any case, we 
> will have to add something to NOTICE.txt if we use it.

It's an ASL 1.1 like license :

http://cvs.spice.codehaus.org/browse/spice/spice/LICENSE.txt?r=1.5

I'll update the NOTICE.txt file.

Emmanuel Bourg


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r647641 - in /commons/proper/configuration/branches/configuration2_experimental: ./ src/main/java/org/apache/commons/configuration2/ src/test/java/org/apache/commons/configuration2/ xdocs/

Posted by Oliver Heger <ol...@oliver-heger.de>.
Some comments:

- I wonder whether it is possible to implement the JNDINodeHandler so 
that it directly operates on Context objects. The main reason why I 
introduced NodeHandlers was that it would no longer be necessary to 
implement specific node classes. This would also be more efficient in 
terms of memory usage because there is no need to duplicate the node 
structure.

- A minor nit: I would prefer if the node handler returned empty lists 
instead of null (e.g. for attributes). This simplifies the code in 
AbstractHierarchicalConfiguration. If the node handler returns 
Collections.emptyList(), there should not be any performance issues.

- What exactly is the license of the spice JNDI kit? In any case, we 
will have to add something to NOTICE.txt if we use it.

Oliver

ebourg@apache.org schrieb:
> 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
> 
<snip/>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org