You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ps...@apache.org on 2005/02/13 00:30:34 UTC

svn commit: r153579 - in incubator/directory/naming/trunk: naming-config/src/conf/ naming-config/src/java/org/apache/naming/config/ naming-config/src/test/ naming-config/src/test/org/apache/naming/config/ naming-core/src/java/org/apache/naming/ naming-core/src/test/org/apache/naming/ xdocs/

Author: psteitz
Date: Sat Feb 12 15:30:30 2005
New Revision: 153579

URL: http://svn.apache.org/viewcvs?view=rev&rev=153579
Log:
* Added support for named contexts to XMLConfigurator (DIRNAMING-12).
* Changed loadConfiguration to use update semantics
* Fixed bug in destroyContext method reported by Roland Berger.
* Added ContextUtils class of static methods to manipulate contexts.
* Added methods to ContextBindings to get bound context names, clear
  context bindings and destroy bound contexts.


Added:
    incubator/directory/naming/trunk/naming-config/src/test/test-jndi1.xml
    incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextUtils.java
    incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextUtilsTest.java
Modified:
    incubator/directory/naming/trunk/naming-config/src/conf/naming.dtd
    incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/Config.java
    incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/XmlConfigurator.java
    incubator/directory/naming/trunk/naming-config/src/test/org/apache/naming/config/XmlConfiguratorTest.java
    incubator/directory/naming/trunk/naming-config/src/test/test-jndi.xml
    incubator/directory/naming/trunk/naming-config/src/test/test-jndi2.xml
    incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextBindings.java
    incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextBindingsTest.java
    incubator/directory/naming/trunk/xdocs/changes.xml

Modified: incubator/directory/naming/trunk/naming-config/src/conf/naming.dtd
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/conf/naming.dtd?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/conf/naming.dtd (original)
+++ incubator/directory/naming/trunk/naming-config/src/conf/naming.dtd Sat Feb 12 15:30:30 2005
@@ -1,6 +1,6 @@
 <!ELEMENT naming (context*)>
 
-<!ELEMENT context ((context|environment|resource)*)>
+<!ELEMENT context ((environment|resource)*)>
 <!ATTLIST context
 	name  CDATA #IMPLIED>
 

Modified: incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/Config.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/Config.java?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/Config.java (original)
+++ incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/Config.java Sat Feb 12 15:30:30 2005
@@ -28,9 +28,11 @@
 import javax.naming.CompositeName;
 import javax.naming.InvalidNameException;
 import javax.naming.StringRefAddr;
-
+
+import org.apache.commons.collections.map.UnmodifiableMap;
 import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
+import org.apache.commons.lang.builder.ToStringStyle;
+
 import org.apache.naming.ResourceRef;
 
 /**
@@ -48,7 +50,7 @@
     {
         /** list of context configurations */
         private final Collection contextList = new LinkedList();
-
+        
         /**
          *  Adds a new Context configuration to the context list.
          * 
@@ -68,24 +70,27 @@
         {
             return Collections.unmodifiableCollection(contextList);
         }
-
+        
         /**
-         * Generates and returns a sorted list of all configured names.
+         * Generates and returns a map, keyed on context name, of sorted sets
+         * of all configured names.
          * 
-         * @return configured names
+         * @return map of sets of configured names, keyed on context name.
          * @throws InvalidNameException if an invalid name is encountered
          */
-        public Set generateSortedSubcontextNameSet() throws InvalidNameException
+        public Map generateSortedSubcontextNameSet() throws InvalidNameException
         {
-            Set sortedSubcontextNameSet = new TreeSet();
+            Map sortedSubcontextNameMap = new HashMap();
             for (Iterator i = contextList.iterator(); i.hasNext();)
-            {
+            {
+                Set sortedSubcontextNameSet = new TreeSet();
                 Context context = (Context) i.next();
-                context.addSubContextNames(sortedSubcontextNameSet);
+                context.addSubContextNames(sortedSubcontextNameSet);
+                sortedSubcontextNameMap.put(context.getName(), sortedSubcontextNameSet);
             }
-            return Collections.unmodifiableSet(sortedSubcontextNameSet);
+            return UnmodifiableMap.decorate(sortedSubcontextNameMap);
         }
-
+        
         /**
          * Returns a string representation of the context list.
          * 
@@ -94,22 +99,28 @@
         public String toString()
         {
             return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-                .append("contextList", contextList)
-                .toString();
+            .append("contextList", contextList)
+            .toString();
         }
     }
-
+    
     /**
      * Configuration for a Context.  Contexts contain lists of 
-     * {@link org.apache.naming.config.Config$Environment} entries and 
-     * {@link org.apache.naming.config.Config$Resource} references.
+     * {@link org.apache.naming.config.Config$Environment} entries,
+     * {@link org.apache.naming.config.Config$Resource} references, and
+     * {@link org.apache.naming.config.Config$Link} links.
      */
     public static final class Context
-    {
-        private String name;
+    {
+        /** External name of the context -- key in ContextBindings */
+        private String name;
+        
+        /** Base name relative to which property and resource bindings are set up */
+        private String base;
+        
         private final Collection environmentList = new LinkedList();
         private final Collection resourceList = new LinkedList();
-
+        
         /**
          * Adds an Environment configuration to the environment list.
          * 
@@ -119,7 +130,7 @@
         {
             environmentList.add(environment);
         }
-
+        
         /**
          * Adds the subcontext names in this Context to the input set.
          * 
@@ -147,13 +158,13 @@
                 addSubContextNames(name, sortedSubcontextNameSet);
             }
         }
-
+        
         private void addSubContextNames(CompositeName name, Set sortedSubcontextNameSet) {
             for (int j = 1; j <= name.size() - 1; j++) {
                 sortedSubcontextNameSet.add(name.getPrefix(j).toString());
             }
         }
-
+        
         /**
          * Adds a Resource configuration to the resource list.
          * 
@@ -164,6 +175,7 @@
             resourceList.add(resource);
         }
         
+        
         /**
          * Returns the environment list.
          * 
@@ -173,7 +185,7 @@
         {
             return Collections.unmodifiableCollection(environmentList);
         }
-
+        
         /**
          * Returns the name of this context.
          * 
@@ -183,7 +195,7 @@
         {
             return name;
         }
-
+        
         /**
          * Sets the name of this context.
          * 
@@ -193,7 +205,7 @@
         {
             this.name = name;
         }
-
+        
         /**
          * Returns the resource list.
          * 
@@ -203,7 +215,7 @@
         {
             return Collections.unmodifiableCollection(resourceList);
         }
-
+        
         /**
          * Returns a string representation of the name, environment list and
          * resource list of this context.
@@ -213,13 +225,27 @@
         public String toString()
         {
             return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-                .append("name", name)
-                .append("environmentList", environmentList)
-                .append("resourceList", resourceList)
-                .toString();
+            .append("name", name)
+            .append("environmentList", environmentList)
+            .append("resourceList", resourceList)
+            .toString();
         }
+        /**
+         * @return Returns the base.
+         */
+        public String getBase() {
+            return base;
+        }
+        
+        /**
+         * @param base The base to set.
+         */
+        public void setBase(String base) {
+            this.base = base;
+        }
+        
     }
-
+    
     /**
      * Configuration for an Environment entry.  Environment entries represent
      * JNDI environment properties that take values that are primitive java
@@ -241,7 +267,7 @@
         {
             return name;
         }
-
+        
         /**
          * Sets the name of this environment.
          * 
@@ -251,7 +277,7 @@
         {
             this.name = name;
         }
-
+        
         /**
          * Returns the class name of this environment entry.
          * 
@@ -261,7 +287,7 @@
         {
             return type;
         }
-
+        
         /**
          * Sets the class name of this environment entry.
          * 
@@ -271,7 +297,7 @@
         {
             this.type = type;
         }
-
+        
         /**
          * Returns the value of this environment entry as a String.
          * 
@@ -281,7 +307,7 @@
         {
             return value;
         }
-
+        
         /**
          * Sets the (String) value of this environment entry.
          * 
@@ -291,7 +317,7 @@
         {
             this.value = value;
         }
-
+        
         /**
          * Returns the JNDI name, type and value of this environment entry as
          * as String.
@@ -301,12 +327,12 @@
         public String toString()
         {
             return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-                .append("name", name)
-                .append("type", type)
-                .append("value", value)
-                .toString();
+            .append("name", name)
+            .append("type", type)
+            .append("value", value)
+            .toString();
         }
-
+        
         /**
          * Tries to create an instance of type <code>this.type</code> using 
          * <code>this.value</code>. 
@@ -360,19 +386,19 @@
             return null;
         }
     }
-
+    
     /**
-    * Configuration for an JNDI resource reference.  Resource references 
-    * include the type of the resource, the parameters to be used in creating
-    * the resource instance and the JNDI name of the resource as a string, 
-    * relative to the initial context.
-    */
+     * Configuration for a JNDI resource reference.  Resource references 
+     * include the type of the resource, the parameters to be used in creating
+     * the resource instance and the JNDI name of the resource as a string, 
+     * relative to the initial context.
+     */
     public static final class Resource
     {
         private String name;
         private String type;
         private final Map parameters = new HashMap();
-
+        
         /**
          * Adds a name-value pair to the parameters associated with this resource.
          * 
@@ -393,7 +419,7 @@
         {
             return name;
         }
-
+        
         /**
          * Sets the name of this resource.
          * 
@@ -403,7 +429,7 @@
         {
             this.name = name;
         }
-
+        
         /**
          * Returns the parameters associated with this resource as a Map.
          * The keys of the map are the parameter names.
@@ -414,7 +440,7 @@
         {
             return parameters;
         }
-
+        
         /**
          * Returns the type of this resource.
          * 
@@ -424,7 +450,7 @@
         {
             return type;
         }
-
+        
         /**
          * Sets the type of this resource.
          * 
@@ -434,13 +460,13 @@
         {
             this.type = type;
         }
-
-       /**
-        * Creates a {@link ResourceRef} based on the configuration
-        * properties of this resource.
-        * 
-        * @return ResourceRef instance.
-        */
+        
+        /**
+         * Creates a {@link ResourceRef} based on the configuration
+         * properties of this resource.
+         * 
+         * @return ResourceRef instance.
+         */
         public Object createValue()
         //TODO: exceptions?
         {
@@ -453,7 +479,7 @@
             }
             return ref;
         }
-
+        
         /**
          * Returns the name, type and parameter list as a String.
          * 
@@ -462,11 +488,11 @@
         public String toString()
         {
             return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-                .append("name", name)
-                .append("type", type)
-                .append("parameters", parameters)
-                .toString();
-        }
+            .append("name", name)
+            .append("type", type)
+            .append("parameters", parameters)
+            .toString();
+        }
     }
 }
 

Modified: incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/XmlConfigurator.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/XmlConfigurator.java?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/XmlConfigurator.java (original)
+++ incubator/directory/naming/trunk/naming-config/src/java/org/apache/naming/config/XmlConfigurator.java Sat Feb 12 15:30:30 2005
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999,2004 The Apache Software Foundation.
+ * Copyright 2004 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,19 +14,19 @@
  * limitations under the License.
  */ 
 
-
 package org.apache.naming.config;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.Name;
-import javax.naming.NameClassPair;
-import javax.naming.NamingEnumeration;
+import javax.naming.NameAlreadyBoundException;
 import javax.naming.NamingException;
 
 import org.apache.commons.digester.Digester;
@@ -35,14 +35,28 @@
 
 import org.xml.sax.SAXException;
 
+import org.apache.naming.ContextBindings;
+import org.apache.naming.ContextUtils;
+import org.apache.naming.NamingContextFactory;
+
 /**
  * Configure an in-memory JNDI implementation using an XML configuration file.
+ * <p>
+ * Multiple named contexts can be configured, and names configured in the file
+ * can be made relative to a base name.  Use the "base" attribute of the context
+ * element in the xml config to specify a base name and use the "name" attribute
+ * to specify an external name for the context, which will be its key in 
+ * {@link org.apache.naming.ContextBindings}.  Configurations added using 
+ * {@link #loadConfiguration} add / update data for contexts already defined.
+ * To destroy contexts already defined, use either 
+ * {@link #destroyInitialContext()}, which destroys the "anonymous" context
+ * (what new InitialContext() returns); or {@link #destroyAll}, which destroys
+ * all defined contexts (named or "anonymous").
  * 
  * @author <a href="brett@apache.org">Brett Porter</a>
  * @version $Id: XmlConfigurator.java,v 1.2 2003/12/01 02:02:45 brett Exp $
  */
-public class XmlConfigurator
-{
+public class XmlConfigurator {
     private static final String COMP_CONTEXT_NAME = "java:comp";
     private static final String ENV_CONTEXT_NAME = "env";
     private static final String ROOT_ELEMENT = "naming";
@@ -55,59 +69,64 @@
     private static final Log LOG = LogFactory.getLog(XmlConfigurator.class);
 
     /**
-     * Sets up initial context using 
-     * <code>org.apache.naming.java.javaURLContextFactory</code>.
+     * Sets up initial context using <code>org.apache.naming.NamingContextFactory</code>.
      * <p>
      * Also creates "env" subcontext in "java:comp" namespace.
      * 
      * @throws NamingException if a NamingException occurs.
      */
-    public static synchronized void setupInitialContext() throws NamingException {
-        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
-        System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
-
+    public static synchronized void setupInitialContext()
+        throws NamingException {
+        setSystemProperties();
         Context initialContext = new InitialContext();
-        envContext = initialContext.createSubcontext(COMP_CONTEXT_NAME).createSubcontext(ENV_CONTEXT_NAME);
+        envContext = initialContext.createSubcontext(
+                COMP_CONTEXT_NAME).createSubcontext(ENV_CONTEXT_NAME);
     }
 
     /**
-     * Destroys initial context.
-     * <p>
-     * Invokes <code>Context.destroySubcontext(Name)</code> only on top-level
-     * subcontexts.
+     * Destroys "anonymous" initial context, destoying subcontexts recursively.
      * 
-     * @throws NamingException if a NamingException occurs.
+     * @throws NamingException if a NamingException occurs
      */
-    public static synchronized void destroyInitialContext() throws NamingException {
-        Context initialContext = new InitialContext();
-        NamingEnumeration contexts = initialContext.list("");
-        while (contexts.hasMore()) {
-            initialContext.destroySubcontext(((NameClassPair) contexts.next()).getName());          
-        }
-        envContext = null;    
-        initialContext = null;
+    public static synchronized void destroyInitialContext()
+        throws NamingException {
+        ContextUtils.destroyInitialContext();
     }
 
     /**
-     * Loads xml configuration data from <code>inputFile</code> into initial context.
+     * Destroys all named contexts as well as "anonymous" context.
      * 
-     * @param inputFile  input xml configuration file
-     * @throws NamingException if a NamingException occurs.
-     * @throws ParseException if an error occurs parsing the configuration file.
+     * @throws NamingException if a naming exception occurs
      */
-    public static synchronized void loadConfiguration(InputStream inputFile) throws NamingException, ParseException {
-        if (envContext == null)
-        {
-            setupInitialContext();
-        }
+    public static synchronized void destroyAll() throws NamingException {
+        ContextBindings.destroyBoundContexts();
+        ContextUtils.destroyInitialContext();
+    }
+
+    /**
+     * Sets System JNDI properties.
+     */
+    protected static synchronized void setSystemProperties() {
+        System.setProperty(
+            Context.INITIAL_CONTEXT_FACTORY,
+            "org.apache.naming.NamingContextFactory");
+        System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
+    }
 
+    /**
+     * Parses input file, returning a <code>Config.Naming</code> structure.
+     * 
+     * @param inputFile file to be parsed
+     * @return  Config.Naming representing the config data in the file
+     * @throws ParseException  if an error occurs parsing the file
+     */
+    protected static synchronized Config.Naming parseFile(
+            InputStream inputFile) throws ParseException {
         Digester digester = new Digester();
-// TODO: string constants
         digester.addObjectCreate(ROOT_ELEMENT, Config.Naming.class);
         digester.addObjectCreate(CONTEXT_ELEMENT, Config.Context.class);
         digester.addSetProperties(CONTEXT_ELEMENT);
         digester.addSetNext(CONTEXT_ELEMENT, "addContext");
-        // TODO: handle context inside context?
         digester.addObjectCreate(ENV_ELEMENT, Config.Environment.class);
         digester.addSetProperties(ENV_ELEMENT);
         digester.addSetNext(ENV_ELEMENT, "addEnvironment");
@@ -118,64 +137,138 @@
         digester.addCallParam(RES_PARAM_ELEMENT + "/name", 0);
         digester.addCallParam(RES_PARAM_ELEMENT + "/value", 1);
 
-        try
-        {
+        try {
             Config.Naming naming = (Config.Naming) digester.parse(inputFile);
             if (naming == null) {
-                throw new ParseException("Unable to find root element '" + ROOT_ELEMENT + "'");
+                throw new ParseException(
+                    "Unable to find root element '" + ROOT_ELEMENT + "'");
             }
             if (LOG.isDebugEnabled()) {
                 LOG.debug("XML configuration loaded: " + naming.toString());
             }
+            return naming;
+        } catch (IOException e) {
+            throw new ParseException("Error reading configuration file", e);
+        } catch (SAXException e) {
+            throw new ParseException("Error reading configuration file", e);
+        }
+    }
 
+    /**
+     * Creates naming contexts based on the configuration data in the
+     * input <code>Config.Naming</code> structure.  
+     * <p>
+     * Uses update semantics -- i.e., contexts / entries that already exist are
+     * updated with the data in the input configuration.
+     * 
+     * @param naming  Config.Naming structure containing the context 
+     * configuration data
+     * @throws NamingException  if a NamingException occurs
+     */
+    protected static synchronized void makeContexts(Config.Naming naming)
+        throws NamingException {
+        
+        // Get the map of sets of sorted context names, keyed on external name
+        Map sortedContextNames = naming.generateSortedSubcontextNameSet();
+        
+        // Create named contexts
+        Hashtable env = new Hashtable();
+        for (Iterator i = naming.getContextList().iterator(); i.hasNext();) {
+            env.clear();
+            Config.Context ctx = (Config.Context) i.next();
+            Context jndiCtx = envContext;
+            if (ctx.getName() != null) {
+                env.put(NamingContextFactory.NAME, ctx.getName());
+            } else {
+                env.put( NamingContextFactory.NAME,
+                    NamingContextFactory.DEFAULT_NAME);
+            }
 
-            for (Iterator i = naming.getContextList().iterator(); i.hasNext();)
-            {
-                Config.Context ctx = (Config.Context) i.next();
-                Context jndiCtx = envContext;
-                if (ctx.getName() != null)
-                {
-                    destroyInitialContext();
-                    Context initialContext = new InitialContext();
-                    Name nm = initialContext.getNameParser("").parse(ctx.getName());
+            if (ctx.getBase() != null) {
+                Context initialContext = new InitialContext(env);
+                Name nm = initialContext.getNameParser("").parse(ctx.getBase());
+                try {
                     envContext = initialContext.createSubcontext(nm.get(0));
-                    jndiCtx = envContext;
-                    for (int k = 1; k < nm.size(); k++) {
+                } catch (NameAlreadyBoundException e) {
+                    envContext = (Context) initialContext.lookup(nm.get(0));
+                }
+                jndiCtx = envContext;
+                for (int k = 1; k < nm.size(); k++) {
+                    try {
                         jndiCtx = jndiCtx.createSubcontext(nm.get(k));
+                    } catch (NameAlreadyBoundException e) {
+                        jndiCtx = (Context) jndiCtx.lookup(nm.get(k));
                     }
                 }
-                precreateSubcontextTree(jndiCtx, naming.generateSortedSubcontextNameSet());
-
-                for (Iterator j = ctx.getEnvironmentList().iterator(); j.hasNext();)
-                {
-                    Config.Environment e = (Config.Environment) j.next();
-                    jndiCtx.rebind(e.getName(), e.createValue());
+            } else {
+                Context initialContext = new InitialContext(env);
+                try {
+                    envContext = initialContext.createSubcontext(
+                            COMP_CONTEXT_NAME).createSubcontext(
+                            ENV_CONTEXT_NAME);
+                } catch (NameAlreadyBoundException e) {
+                    envContext = (Context) initialContext.lookup(
+                            COMP_CONTEXT_NAME + "/" + ENV_CONTEXT_NAME);
                 }
+                jndiCtx = envContext;
+            }
 
-                for (Iterator j = ctx.getResourceList().iterator(); j.hasNext();)
-                {
-                    Config.Resource r = (Config.Resource) j.next();
-                    jndiCtx.bind(r.getName(), r.createValue());
-                }
+            precreateSubcontextTree( jndiCtx, 
+                    (Set) sortedContextNames.get( ctx.getName()));
+
+            for (Iterator j = ctx.getEnvironmentList().iterator();
+                j.hasNext();) {
+                Config.Environment e = (Config.Environment) j.next();
+                jndiCtx.rebind(e.getName(), e.createValue());
+            }
+
+            for (Iterator j = ctx.getResourceList().iterator(); j.hasNext();) {
+                Config.Resource r = (Config.Resource) j.next();
+                jndiCtx.rebind(r.getName(), r.createValue());
             }
         }
-        catch (IOException e)
-        {
-            throw new ParseException("Error reading configuration file", e);
-        }
-        catch (SAXException e)
-        {
-            throw new ParseException("Error reading configuration file", e);
-        }
     }
 
-    private static void precreateSubcontextTree(Context ctx, Set sortedSubcontextNameSet) throws NamingException
-    {
-        // TODO: don't recreate
-        for (Iterator i = sortedSubcontextNameSet.iterator(); i.hasNext();)
-        {
+    /**
+     * Loads xml configuration data from <code>inputFile</code>. Uses update
+     * semantics -- i.e., does not clear and create new context(s) and
+     * bindings,  but adds to current definitions, overwriting previous
+     * configuration when contexts or bindings already exist. To clear existing
+     * configuration, use {@link #destroyInitialContext} or {@link #destroyAll}.
+     * 
+     * @param inputFile input xml configuration file
+     * @throws NamingException if a NamingException occurs.
+     * @throws ParseException if an error occurs parsing the configuration file.
+     */
+    public static synchronized void loadConfiguration(InputStream inputFile)
+        throws NamingException, ParseException {
+        // Set system properties
+        setSystemProperties();
+        // Parse file and create contexts
+        makeContexts(parseFile(inputFile));
+    }
+
+    /**
+     * Creates all subcontexts named in <code>sortedSubcontextNameSet</code>.
+     * Some / all subcontexts may already exist. NamingException is only thrown
+     * if the names are invalid or required intermediary contexts are missing.
+     * 
+     * @param ctx context
+     * @param sortedSubcontextNameSet set of names to Create
+     * @throws NamingException if names are invalid required intermediate
+     *  contexts are missing
+     */
+    private static void precreateSubcontextTree(
+        Context ctx,
+        Set sortedSubcontextNameSet)
+        throws NamingException {
+        for (Iterator i = sortedSubcontextNameSet.iterator(); i.hasNext();) {
             String name = (String) i.next();
-            ctx.createSubcontext(name);
+            try {
+                ctx.createSubcontext(name);
+            } catch (NameAlreadyBoundException e) {
+                // ignore -- already created
+            }
         }
     }
 

Modified: incubator/directory/naming/trunk/naming-config/src/test/org/apache/naming/config/XmlConfiguratorTest.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/test/org/apache/naming/config/XmlConfiguratorTest.java?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/test/org/apache/naming/config/XmlConfiguratorTest.java (original)
+++ incubator/directory/naming/trunk/naming-config/src/test/org/apache/naming/config/XmlConfiguratorTest.java Sat Feb 12 15:30:30 2005
@@ -19,13 +19,18 @@
 
 import java.sql.Connection;
 import java.sql.ResultSet;
-import java.sql.Statement;
+import java.sql.Statement;
+
+import java.util.Hashtable;
 
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.sql.DataSource;
 
-import junit.framework.TestCase;
+import junit.framework.TestCase;
+
+import org.apache.naming.ContextBindings;
+import org.apache.naming.NamingContextFactory;
 
 /**
  * Test case for the XML configuration methods, testing environment entries
@@ -70,18 +75,39 @@
 
     /**
      * Test for correctly configured environment entries.
+     * 
      * @throws Exception as tests do
      */
     public void testEnvironment() throws Exception {
         XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi.xml"));
-        checkEnvironment(DEFAULT_ROOT);
+        checkEnvironment(DEFAULT_ROOT, new InitialContext());
         XmlConfigurator.destroyInitialContext();
+        
         XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi2.xml"));
-        checkEnvironment(ALT_ROOT);
+        checkEnvironment(ALT_ROOT, new InitialContext());
+        
+        XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi1.xml"));
+        // "named" global context has same environment entries -- verify
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY,
+        "org.apache.naming.NamingContextFactory");
+        env.put(Context.URL_PKG_PREFIXES, "org.apache.naming");
+        env.put(NamingContextFactory.NAME, "global"); //name attribute 
+        InitialContext ctx = new InitialContext(env);
+        checkEnvironment(DEFAULT_ROOT, new InitialContext(env));
+        
+        // "anonymous" context entries should be overwritten now
+        ctx = new InitialContext();
+        Context envCtx = (Context) ctx.lookup(ALT_ROOT);
+        String host = (String) envCtx.lookup("config/host");
+        Integer port = (Integer) envCtx.lookup("config/port");
+        assertEquals("Check host", "www.jakarta.org", host);
+        assertEquals("Check port", new Integer(85), port);
+        
+        ContextBindings.destroyBoundContexts();
     }
     
-    protected void checkEnvironment(String root) throws Exception {
-        Context ctx = new InitialContext();
+    protected void checkEnvironment(String root, Context ctx) throws Exception {
         Context env = (Context) ctx.lookup(root);
         String host = (String) env.lookup("config/host");
         Integer port = (Integer) env.lookup("config/port");
@@ -106,7 +132,8 @@
      */
     public void testDuplicateSubcontextName() throws Exception{
         XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi.xml"));
-        checkDuplicateSubcontextName(DEFAULT_ROOT);     
+        checkDuplicateSubcontextName(DEFAULT_ROOT);    
+        XmlConfigurator.destroyInitialContext();
     }
     
     protected void checkDuplicateSubcontextName(String root) throws Exception {
@@ -125,14 +152,23 @@
      */
     public void testJdbc() throws Exception {
         XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi.xml"));
-        checkJdbc(DEFAULT_ROOT);
+        checkJdbc(DEFAULT_ROOT, new InitialContext());
         XmlConfigurator.destroyInitialContext();
         XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi2.xml"));
-        checkJdbc(ALT_ROOT);
+        checkJdbc(ALT_ROOT, new InitialContext());
+        XmlConfigurator.destroyInitialContext();
+        XmlConfigurator.loadConfiguration(getClass().getResourceAsStream("/test-jndi1.xml"));
+        checkJdbc(ALT_ROOT, new InitialContext());
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY,
+        "org.apache.naming.NamingContextFactory");
+        env.put(Context.URL_PKG_PREFIXES, "org.apache.naming");
+        env.put(NamingContextFactory.NAME, "global"); //name attribute 
+        checkJdbc(DEFAULT_ROOT + "/global", new InitialContext(env));
+        ContextBindings.destroyBoundContexts();  
     }
     
-    protected void checkJdbc(String root) throws Exception {
-        Context ctx = new InitialContext();
+    protected void checkJdbc(String root, Context ctx) throws Exception {
         Context env = (Context) ctx.lookup(root);
         DataSource ds = (DataSource) env.lookup("jdbc/pool");
         Connection con = null;

Modified: incubator/directory/naming/trunk/naming-config/src/test/test-jndi.xml
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/test/test-jndi.xml?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/test/test-jndi.xml (original)
+++ incubator/directory/naming/trunk/naming-config/src/test/test-jndi.xml Sat Feb 12 15:30:30 2005
@@ -1,11 +1,5 @@
 <naming>
   <context>
-    <context name="sub/context">
-    
-      <environment name="config/host" value="jakarta.apache.org" type="java.lang.String" />
-      <environment name="config/port" value="8000" type="java.lang.Integer" />
-    </context>
-
     <environment name="config/host" value="www.apache.org" type="java.lang.String" />
     <environment name="config/mytruebool" value="true" type="java.lang.Boolean" />
     <environment name="config/myfalsebool" value="false" type="java.lang.Boolean" />

Added: incubator/directory/naming/trunk/naming-config/src/test/test-jndi1.xml
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/test/test-jndi1.xml?view=auto&rev=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/test/test-jndi1.xml (added)
+++ incubator/directory/naming/trunk/naming-config/src/test/test-jndi1.xml Sat Feb 12 15:30:30 2005
@@ -0,0 +1,57 @@
+<naming>
+  <!-- 
+       Anonymous context with base specified, different environment values
+       from test-jndi2.  Loading after jnti2 should overwrite environment entries.
+   -->
+  <context base="alt/root/context">
+    <environment name="config/host" value="www.jakarta.org" type="java.lang.String" />
+    <environment name="config/port" value="85" type="java.lang.Integer" />
+    <resource name="jdbc/pool" type="javax.sql.DataSource">
+      <parameter>
+        <name>driverClassName</name>
+        <value>org.hsqldb.jdbcDriver</value>
+      </parameter>
+      <parameter>
+        <name>url</name>
+        <value>jdbc:hsqldb:target/hsqldb</value>
+      </parameter>
+      <parameter>
+        <name>username</name>
+        <value>sa</value>
+      </parameter>
+      <parameter>
+        <name>password</name>
+        <value></value>
+      </parameter>
+    </resource>
+  </context>
+  <!-- 
+       Add another named context, default root, same environment entries
+        as jndi2.
+   -->
+  <context name="global" >
+    <environment name="config/host" value="www.apache.org" type="java.lang.String" />
+    <environment name="config/mytruebool" value="true" type="java.lang.Boolean" />
+    <environment name="config/myfalsebool" value="false" type="java.lang.Boolean" />
+    <environment name="config/port" value="80" type="java.lang.Integer" />
+    <environment name="jdbc/config/pool/user" value="dbuser" type="java.lang.String" />
+    <resource name="global/jdbc/pool" type="javax.sql.DataSource">
+      <parameter>
+        <name>driverClassName</name>
+        <value>org.hsqldb.jdbcDriver</value>
+      </parameter>
+      <parameter>
+        <name>url</name>
+        <value>jdbc:hsqldb:target/hsqldb</value>
+      </parameter>
+      <parameter>
+        <name>username</name>
+        <value>sa</value>
+      </parameter>
+      <parameter>
+        <name>password</name>
+        <value></value>
+      </parameter>
+    </resource>
+  </context> 
+</naming>

Modified: incubator/directory/naming/trunk/naming-config/src/test/test-jndi2.xml
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-config/src/test/test-jndi2.xml?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-config/src/test/test-jndi2.xml (original)
+++ incubator/directory/naming/trunk/naming-config/src/test/test-jndi2.xml Sat Feb 12 15:30:30 2005
@@ -1,5 +1,5 @@
 <naming>
-  <context name="alt/root/context">
+  <context base="alt/root/context">
     <environment name="config/host" value="www.apache.org" type="java.lang.String" />
     <environment name="config/mytruebool" value="true" type="java.lang.Boolean" />
     <environment name="config/myfalsebool" value="false" type="java.lang.Boolean" />

Modified: incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextBindings.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextBindings.java?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextBindings.java (original)
+++ incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextBindings.java Sat Feb 12 15:30:30 2005
@@ -17,7 +17,10 @@
 
 package org.apache.naming;
 
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.Set;
 import javax.naming.NamingException;
 import javax.naming.Context;
 
@@ -498,6 +501,35 @@
         return false;
     }
     
-
+    /**
+     * Returns the names of bound contexts as an unmodifiable set.
+     * 
+     * @return names of bound contexts
+     */
+    public static Set getContextNames() {
+         return Collections.unmodifiableSet(contextNameBindings.keySet());
+    }
+    
+    /**
+     * Clears context bindings.
+     * 
+     */
+    public static synchronized void clearContextBindings() {
+        contextNameBindings.clear();
+    }
+    
+    /**
+     * Destroys all bound contexts and removes bindings.
+     * 
+     * @throws NamingException
+     */
+    public static synchronized void destroyBoundContexts() throws NamingException {
+        Enumeration contexts = contextNameBindings.elements();
+        while (contexts.hasMoreElements()) {
+            ContextUtils.destroyContext((Context) contexts.nextElement());
+        } 
+        clearContextBindings();
+    }
+    
 }
 

Added: incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextUtils.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextUtils.java?view=auto&rev=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextUtils.java (added)
+++ incubator/directory/naming/trunk/naming-core/src/java/org/apache/naming/ContextUtils.java Sat Feb 12 15:30:30 2005
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.naming;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Hashtable;
+
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.ContextNotEmptyException;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+
+import org.apache.naming.ContextBindings;
+import org.apache.naming.NamingContextFactory;
+
+/**
+ * Collection of static methods that manipulate naming contexts.
+ */
+public class ContextUtils {
+   
+    /*
+     * Prevent instantiation.
+     */
+    private ContextUtils() {}
+   
+    /**
+     * Destroys a context, recursively destroying all subcontexts.
+     * Does not attempt to destroy foreign contexts named by link references.
+     *
+     * @param context context to destroy recursively
+     * @throws NamingException
+     */
+    public static synchronized void destroyContext(Context context)
+    throws NamingException {
+        NamingEnumeration contexts =  context.listBindings("");
+        List bindings = new ArrayList();
+        while (contexts.hasMore()) {
+            bindings.add(contexts.next());
+        }      
+        for (int i =0; i < bindings.size(); i++) {
+            Binding binding = (Binding) bindings.get(i);
+            Object obj = binding.getObject();
+            String name = binding.getName();
+            try {
+                context.destroySubcontext(name);  
+            } catch (NotContextException ex) {
+                context.unbind(name);
+            } catch (ContextNotEmptyException e) {
+                destroyContext((Context) obj);
+            }    
+        } 
+    }
+    
+    /**
+     * Destroys the context with the given external name and removes it from
+     * the context bindings table.
+     * 
+     * @param name external name (i.e. key in {@link ContextBindings} of the
+     * context to destroy
+     * @throws NamingException
+     */
+    public static synchronized void destroyContext(String name) 
+    throws NamingException {
+        Context context = ContextBindings.getContext(name);
+        if (context == null) {
+            throw new NamingException("Named context not found");
+        }
+        destroyContext(context);
+        ContextBindings.unbindContext(name);
+    }
+    
+    /**
+     * Destroys initial context, destoying subcontexts recursively.
+     * 
+     * @throws NamingException if a NamingException occurs
+     */
+    public static synchronized void destroyInitialContext() throws NamingException {
+        Context initialContext = new InitialContext();
+        destroyContext(initialContext);
+        ContextBindings.unbindContext(NamingContextFactory.DEFAULT_NAME);
+    }
+    
+    /**
+     * Destroys the initial context based on the given environment, destoying
+     * subcontexts recursively.  Removes context binding if environment contains
+     * the NamingContextFactory.NAME key.
+     * 
+     * @throws NamingException if a NamingException occurs
+     */
+    public static synchronized void destroyInitialContext(Hashtable env) 
+        throws NamingException {
+        Context initialContext = new InitialContext(env);
+        destroyContext(initialContext);
+        if (env.containsKey(NamingContextFactory.NAME)) {
+            ContextBindings.unbindContext(env.get(NamingContextFactory.NAME));      
+        }
+    }
+   
+}
+

Modified: incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextBindingsTest.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextBindingsTest.java?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextBindingsTest.java (original)
+++ incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextBindingsTest.java Sat Feb 12 15:30:30 2005
@@ -16,8 +16,11 @@
 package org.apache.naming;
 
 import java.util.Hashtable;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.naming.Context;
+import javax.naming.InitialContext;
 import javax.naming.NamingException;
 
 import junit.framework.Test;
@@ -29,7 +32,7 @@
 /**
  * Unit tests for  {@link ContextBindings}.
  *  
- * @version $Revision: 1.2 $ $Date: 2003/11/30 05:36:07 $
+ * @version $Revision$ $Date: 2003/11/30 05:36:07 $
  */
 public class ContextBindingsTest extends TestCase {
     
@@ -62,10 +65,7 @@
     public void tearDown() throws Exception {
         ContextAccessController.unsetSecurityToken(contextName, testToken1);
         ContextAccessController.unsetSecurityToken(subName, testToken2);
-        ContextBindings.unbindContext(contextName);
-        ContextBindings.unbindContext(subName);
-        testContext.destroySubcontext("env:comp");
-        testContext = null;
+        ContextBindings.destroyBoundContexts();
     }
     
    public void testNameBindings() throws Exception {
@@ -210,4 +210,49 @@
        ContextBindings.unbindClassLoader(contextName, testToken1);
        assertFalse(ContextBindings.isClassLoaderBound());   
    } 
+   
+   protected void makeBindings() throws Exception {
+       // "Factory bound"
+       Hashtable env = new Hashtable();
+       env.put(Context.INITIAL_CONTEXT_FACTORY,
+       "org.apache.naming.NamingContextFactory");
+       env.put(Context.URL_PKG_PREFIXES, "org.apache.naming");
+       env.put(NamingContextFactory.NAME, "test1");
+       Context context1 = new InitialContext(env);
+       
+       // Explicit binding
+       ContextBindings.bindContext("test2", testContext); // created in setup
+   }
+   
+   public void testGetContextNames() throws Exception {
+       ContextBindings.clearContextBindings();
+       Set names = ContextBindings.getContextNames();
+       assertTrue(names.isEmpty());
+       makeBindings();
+       names = ContextBindings.getContextNames();
+       HashSet expected = new HashSet();
+       expected.add("test1");
+       expected.add("test2");
+       assertEquals(expected, names);
+       // Verify unmodifiable
+       try {
+           names.remove("test1");
+           fail("Expecting UnsupportedOperationException");
+       } catch (UnsupportedOperationException e) {
+           // Expected
+       }
+       try {
+           names.add("test3");
+           fail("Expecting UnsupportedOperationException");
+       } catch (UnsupportedOperationException e) {
+           // Expected
+       }
+   }
+   
+   public void testDestroyBoundContexts() throws Exception {
+       makeBindings();
+       ContextBindings.destroyBoundContexts();
+       assertTrue(ContextBindings.getContextNames().isEmpty());
+       assertFalse(testContext.list("").hasMoreElements());
+   }
 }

Added: incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextUtilsTest.java
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextUtilsTest.java?view=auto&rev=153579
==============================================================================
--- incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextUtilsTest.java (added)
+++ incubator/directory/naming/trunk/naming-core/src/test/org/apache/naming/ContextUtilsTest.java Sat Feb 12 15:30:30 2005
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.naming;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import junit.framework.TestCase;
+
+
+/**
+ * Unit tests for  {@link ContextUtils}.
+ *  
+ * @version $Revision: 56478 $ $Date: 2003/11/30 05:36:07 $
+ */
+public class ContextUtilsTest extends TestCase {
+    
+    public ContextUtilsTest(String name) {
+        super(name);
+    }
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+    	TestSuite suite = new TestSuite(ContextUtilsTest.class);
+    	suite.setName("ContextUils Tests");
+        return suite;
+    }
+    
+    public void setUp() throws Exception {
+    }
+    
+    public void tearDown() throws Exception {
+    }
+    
+   public void testDestroyInitialContext() throws Exception {
+       // First try with named context
+       Hashtable env = new Hashtable();
+       env.put(NamingContextFactory.NAME, "test");
+       env.put(Context.INITIAL_CONTEXT_FACTORY,
+               "org.apache.naming.NamingContextFactory");
+       env.put(Context.URL_PKG_PREFIXES, "org.apache.naming");
+       checkInitialContext(env);
+       
+       // Anonymous
+       env.clear();
+       env.put(Context.INITIAL_CONTEXT_FACTORY,
+       "org.apache.naming.NamingContextFactory");
+       env.put(Context.URL_PKG_PREFIXES, "org.apache.naming");
+       checkInitialContext(env);
+   }
+   
+   public void testDestroyContext() throws Exception {
+       Hashtable env = new Hashtable();    
+       
+       // Anonymous
+       env.put(Context.INITIAL_CONTEXT_FACTORY,
+       "org.apache.naming.NamingContextFactory");
+       env.put(Context.URL_PKG_PREFIXES, "org.apache.naming");
+       Context initialContext = makeInitialContext(env);
+       Context victim = (Context) initialContext.lookup("sub/next");
+       ContextUtils.destroyContext(victim);
+       try {
+           initialContext.lookup("sub/next");
+           fail("Expecting NamingException");
+       } catch (NamingException ex) {
+           // Expected
+       }
+       ContextUtils.destroyInitialContext(env);
+       
+       // Named
+       initialContext = makeInitialContext(env);
+       victim = (Context) initialContext.lookup("sub/next");
+       ContextBindings.bindContext("victim", victim);
+       ContextUtils.destroyContext("victim");
+       try {
+           initialContext.lookup("sub/next");
+           fail("Expecting NamingException");
+       } catch (NamingException ex) {
+           // Expected
+       }
+       assertNull(ContextBindings.getContext("victim"));   
+       
+       try { 
+           ContextUtils.destroyContext("victim");
+           fail("Expecting NamingException");
+       } catch (NamingException e) {
+           // Expected
+       }
+       
+       ContextUtils.destroyInitialContext(env);
+   }
+   
+   protected void checkInitialContext(Hashtable env) throws Exception {
+       killInitialContext(env, makeInitialContext(env));
+   }
+   
+   protected Context makeInitialContext(Hashtable env) throws Exception {
+       Context initialContext = new InitialContext(env);
+       initialContext.bind("foo", "bar");
+       initialContext.bind("int", new Integer(1));
+       initialContext.createSubcontext("sub");
+       Context subContext = (Context) initialContext.lookup("sub");
+       subContext.bind("foofoo","barbar");
+       subContext.createSubcontext("next");
+       Context nextContext = (Context) subContext.lookup("next");
+       subContext.bind("parent", initialContext);      // cycle
+       nextContext.bind("grandpa", initialContext);  // multi-generational cycle 
+       assertEquals("initial lookup", "barbar", initialContext.lookup("sub/foofoo"));
+       assertEquals("inbred lookup", "barbar", initialContext.lookup("sub/next/grandpa/sub/foofoo")); 
+       return initialContext;
+   }
+   
+   protected void killInitialContext(Hashtable env, Context context) throws Exception {    
+       ContextUtils.destroyInitialContext(env);
+       assertNull(ContextBindings.getContext("test"));
+       assertFalse(context.list("").hasMore());
+   }
+}

Modified: incubator/directory/naming/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewcvs/incubator/directory/naming/trunk/xdocs/changes.xml?view=diff&r1=153578&r2=153579
==============================================================================
--- incubator/directory/naming/trunk/xdocs/changes.xml (original)
+++ incubator/directory/naming/trunk/xdocs/changes.xml Sat Feb 12 15:30:30 2005
@@ -44,6 +44,14 @@
           When using NamingContextFactory, a NAME property may be specified to 
           retrieve or create a named context. 
       </action>
+      <action dev="psteitz" type="update" issue="DIRNAMING-12">
+          Added support for named contexts to XMLConfigurator.
+          Changed loadConfiguration to use update semantics.
+          Fixed bug in destroyContext method.
+          Added ContextUtils class of static methods to manipulate contexts.
+          Added methods to ContextBindings to get bound context names, clear
+          context bindings and destroy bound contexts.
+      </action>
     </release>     
     <release version="0.8" date="2005-01-15" 
       description="Apache Directory Naming 0.8 - Incubator Technology Preview">