commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ebo...@apache.org
Subject 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/
Date Sun, 13 Apr 2008 23:59:51 GMT
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="">



Mime
View raw message