From 5be072c20e46f0921bb00401ff26d0defb3e8991 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Tue, 05 Dec 2006 21:41:50 +0000
Subject: [PATCH] Update the base DN registration process to address a number of issues:

---
 opends/resource/schema/02-config.ldif                                                              |    3 
 opends/src/server/org/opends/server/core/BackendConfigManager.java                                 |  163 +++
 opends/src/server/org/opends/server/backends/BackupBackend.java                                    |   63 
 opends/src/server/org/opends/server/api/Backend.java                                               |   92 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java |  783 ++++++++++++++++++
 opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java                  |    4 
 opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java                     |   11 
 opends/src/server/org/opends/server/backends/MemoryBackend.java                                    |   57 
 opends/src/server/org/opends/server/messages/CoreMessages.java                                     |  136 +++
 opends/src/server/org/opends/server/util/ServerConstants.java                                      |   17 
 opends/src/server/org/opends/server/core/DirectoryServer.java                                      |  710 ++++++++++------
 opends/src/server/org/opends/server/backends/MonitorBackend.java                                   |   63 
 opends/src/server/org/opends/server/messages/ConfigMessages.java                                   |   14 
 opends/src/server/org/opends/server/messages/BackendMessages.java                                  |   14 
 opends/src/server/org/opends/server/backends/jeb/BackendImpl.java                                  |   82 -
 opends/src/server/org/opends/server/core/AddOperation.java                                         |    2 
 opends/src/server/org/opends/server/types/DN.java                                                  |    4 
 opends/src/server/org/opends/server/backends/RootDSEBackend.java                                   |   85 -
 opends/src/server/org/opends/server/extensions/ConfigFileHandler.java                              |   39 
 opends/src/server/org/opends/server/backends/SchemaBackend.java                                    |   73 -
 opends/src/server/org/opends/server/backends/task/TaskBackend.java                                 |   63 
 21 files changed, 1,856 insertions(+), 622 deletions(-)

diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index c510bca..4eea314 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -995,6 +995,9 @@
 attributeTypes: ( 1.3.6.1.4.1.26027.1.1.289 NAME 'ds-pwp-password-policy-dn'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE directoryOperation
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.291 NAME 'ds-private-naming-contexts'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE directoryOperation
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
   MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
diff --git a/opends/src/server/org/opends/server/api/Backend.java b/opends/src/server/org/opends/server/api/Backend.java
index a97914c..eba5623 100644
--- a/opends/src/server/org/opends/server/api/Backend.java
+++ b/opends/src/server/org/opends/server/api/Backend.java
@@ -29,6 +29,7 @@
 
 
 import java.util.ArrayList;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.locks.Lock;
@@ -400,10 +401,18 @@
    * @param  controlOID  The OID of the control for which to make the
    *                     determination.
    *
-   * @return  <CODE>true</CODE> if this backend does support the
-   *          requested control, or <CODE>false</CODE>
+   * @return  {@code true} if this backends supports the control with
+   *          the specified OID, or {@code false} if it does not.
    */
-  public abstract boolean supportsControl(String controlOID);
+  public final boolean supportsControl(String controlOID)
+  {
+    assert debugEnter(CLASS_NAME, "supportsControl",
+                      String.valueOf(controlOID));
+
+    Set<String> supportedControls = getSupportedControls();
+    return ((supportedControls != null) &&
+            supportedControls.contains(controlOID));
+  }
 
 
 
@@ -424,10 +433,18 @@
    * @param  featureOID  The OID of the feature for which to make the
    *                     determination.
    *
-   * @return  <CODE>true</CODE> if this backend does support the
-   *          requested feature, or <CODE>false</CODE>
+   * @return  {@code true} if this backend supports the feature with
+   *          the specified OID, or {@code false} if it does not.
    */
-  public abstract boolean supportsFeature(String featureOID);
+  public final boolean supportsFeature(String featureOID)
+  {
+    assert debugEnter(CLASS_NAME, "supportsFeature",
+                      String.valueOf(featureOID));
+
+    Set<String> supportedFeatures = getSupportedFeatures();
+    return ((supportedFeatures != null) &&
+            supportedFeatures.contains(featureOID));
+  }
 
 
 
@@ -892,15 +909,64 @@
 
     synchronized (this)
     {
-      Backend[] newSubordinateBackends =
-           new Backend[subordinateBackends.length+1];
+      LinkedHashSet<Backend> backendSet =
+           new LinkedHashSet<Backend>();
 
-      System.arraycopy(subordinateBackends, 0, newSubordinateBackends,
-                       0, subordinateBackends.length);
-      newSubordinateBackends[subordinateBackends.length] =
-           subordinateBackend;
+      for (Backend b : subordinateBackends)
+      {
+        backendSet.add(b);
+      }
 
-      subordinateBackends = newSubordinateBackends;
+      if (backendSet.add(subordinateBackend))
+      {
+        Backend[] newSubordinateBackends =
+             new Backend[backendSet.size()];
+        backendSet.toArray(newSubordinateBackends);
+        subordinateBackends = newSubordinateBackends;
+      }
+    }
+  }
+
+
+
+  /**
+   * Removes the provided backend from the set of subordinate backends
+   * for this backend.
+   *
+   * @param  subordinateBackend  The backend to remove from the set of
+   *                             subordinate backends for this
+   *                             backend.
+   */
+  public void removeSubordinateBackend(Backend subordinateBackend)
+  {
+    assert debugEnter(CLASS_NAME, "removeSubordinateBackend",
+                      String.valueOf(subordinateBackend));
+
+    synchronized (this)
+    {
+      ArrayList<Backend> backendList =
+           new ArrayList<Backend>(subordinateBackends.length);
+
+      boolean found = false;
+      for (Backend b : subordinateBackends)
+      {
+        if (b.equals(subordinateBackend))
+        {
+          found = true;
+        }
+        else
+        {
+          backendList.add(b);
+        }
+      }
+
+      if (found)
+      {
+        Backend[] newSubordinateBackends =
+             new Backend[backendList.size()];
+        backendList.toArray(newSubordinateBackends);
+        subordinateBackends = newSubordinateBackends;
+      }
     }
   }
 
diff --git a/opends/src/server/org/opends/server/backends/BackupBackend.java b/opends/src/server/org/opends/server/backends/BackupBackend.java
index fda38ea..0576804 100644
--- a/opends/src/server/org/opends/server/backends/BackupBackend.java
+++ b/opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -275,7 +275,19 @@
 
 
     // Register the backup base as a private suffix.
-    DirectoryServer.registerPrivateSuffix(backupBaseDN, this);
+    try
+    {
+      DirectoryServer.registerBaseDN(backupBaseDN, this, true, false);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "initializeBackend", e);
+
+      msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+      String message = getMessage(msgID, backupBaseDN.toString(),
+                                  stackTraceToSingleLineString(e));
+      throw new InitializationException(msgID, message, e);
+    }
   }
 
 
@@ -296,6 +308,15 @@
     assert debugEnter(CLASS_NAME, "finalizeBackend");
 
     DirectoryServer.deregisterConfigurableComponent(this);
+
+    try
+    {
+      DirectoryServer.deregisterBaseDN(backupBaseDN, false);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "finalizeBackend", e);
+    }
   }
 
 
@@ -1029,26 +1050,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param  controlOID  The OID of the control for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl",
-                      String.valueOf(controlOID));
-
-    // This backend does not provide any special control support.
-    return false;
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the features that may be supported by this backend.
    *
    * @return  The OIDs of the features that may be supported by this backend.
@@ -1063,26 +1064,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param  featureOID  The OID of the feature for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature",
-                      String.valueOf(featureOID));
-
-    // This backend does not provide any special feature support.
-    return false;
-  }
-
-
-
-  /**
    * Indicates whether this backend provides a mechanism to export the data it
    * contains to an LDIF file.
    *
diff --git a/opends/src/server/org/opends/server/backends/MemoryBackend.java b/opends/src/server/org/opends/server/backends/MemoryBackend.java
index a2587c4..10bd317 100644
--- a/opends/src/server/org/opends/server/backends/MemoryBackend.java
+++ b/opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -48,6 +48,7 @@
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
 import org.opends.server.types.LDIFExportConfig;
 import org.opends.server.types.LDIFImportConfig;
 import org.opends.server.types.RestoreConfig;
@@ -62,6 +63,7 @@
 import static org.opends.server.messages.BackendMessages.*;
 import static org.opends.server.messages.MessageHandler.*;
 import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
 
 
 
@@ -148,7 +150,7 @@
    */
   public synchronized void initializeBackend(ConfigEntry configEntry,
                                              DN[] baseDNs)
-         throws ConfigException
+         throws ConfigException, InitializationException
   {
     assert debugEnter(CLASS_NAME, "initializeBackend",
                       String.valueOf(configEntry), String.valueOf(baseDNs));
@@ -184,7 +186,19 @@
 
     for (DN dn : baseDNs)
     {
-      DirectoryServer.registerSuffix(dn, this);
+      try
+      {
+        DirectoryServer.registerBaseDN(dn, this, false, false);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "initializeBackend", e);
+
+        int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+        String message = getMessage(msgID, dn.toString(),
+                                    stackTraceToSingleLineString(e));
+        throw new InitializationException(msgID, message, e);
+      }
     }
   }
 
@@ -209,6 +223,18 @@
     assert debugEnter(CLASS_NAME, "finalizeBackend");
 
     clearMemoryBackend();
+
+    for (DN dn : baseDNs)
+    {
+      try
+      {
+        DirectoryServer.deregisterBaseDN(dn, false);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "finalizeBackend", e);
+      }
+    }
   }
 
 
@@ -585,19 +611,6 @@
   /**
    * {@inheritDoc}
    */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl",
-                      String.valueOf(controlOID));
-
-    return supportedControls.contains(controlOID);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
   public HashSet<String> getSupportedFeatures()
   {
     assert debugEnter(CLASS_NAME, "getSupportedFeatures");
@@ -610,20 +623,6 @@
   /**
    * {@inheritDoc}
    */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature",
-                      String.valueOf(featureOID));
-
-    // This backend does not provide any special feature support.
-    return false;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
   public boolean supportsLDIFExport()
   {
     assert debugEnter(CLASS_NAME, "supportsLDIFExport");
diff --git a/opends/src/server/org/opends/server/backends/MonitorBackend.java b/opends/src/server/org/opends/server/backends/MonitorBackend.java
index 206a778..fd716a9 100644
--- a/opends/src/server/org/opends/server/backends/MonitorBackend.java
+++ b/opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -250,7 +250,19 @@
 
 
     // Register the monitor base as a private suffix.
-    DirectoryServer.registerPrivateSuffix(baseMonitorDN, this);
+    try
+    {
+      DirectoryServer.registerBaseDN(baseMonitorDN, this, true, false);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "initializeBackend", e);
+
+      int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+      String message = getMessage(msgID, baseMonitorDN.toString(),
+                                  stackTraceToSingleLineString(e));
+      throw new InitializationException(msgID, message, e);
+    }
   }
 
 
@@ -271,6 +283,15 @@
     assert debugEnter(CLASS_NAME, "finalizeBackend");
 
     DirectoryServer.deregisterConfigurableComponent(this);
+
+    try
+    {
+      DirectoryServer.deregisterBaseDN(baseMonitorDN, false);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "finalizeBackend", e);
+    }
   }
 
 
@@ -929,26 +950,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param  controlOID  The OID of the control for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl",
-                      String.valueOf(controlOID));
-
-    // This backend does not provide any special control support.
-    return false;
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the features that may be supported by this backend.
    *
    * @return  The OIDs of the features that may be supported by this backend.
@@ -963,26 +964,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param  featureOID  The OID of the feature for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature",
-                      String.valueOf(featureOID));
-
-    // This backend does not provide any special feature support.
-    return false;
-  }
-
-
-
-  /**
    * Indicates whether this backend provides a mechanism to export the data it
    * contains to an LDIF file.
    *
diff --git a/opends/src/server/org/opends/server/backends/RootDSEBackend.java b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
index 5611e60..17aac64 100644
--- a/opends/src/server/org/opends/server/backends/RootDSEBackend.java
+++ b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -502,21 +502,42 @@
 
 
     // Add the "namingContexts" attribute.
-    Attribute namingContextAttr =
+    Attribute publicNamingContextAttr =
          createDNAttribute(ATTR_NAMING_CONTEXTS, ATTR_NAMING_CONTEXTS_LC,
-                           DirectoryServer.getSuffixes().keySet());
-    ArrayList<Attribute> namingContextAttrs = new ArrayList<Attribute>(1);
-    namingContextAttrs.add(namingContextAttr);
+                           DirectoryServer.getPublicNamingContexts().keySet());
+    ArrayList<Attribute> publicNamingContextAttrs = new ArrayList<Attribute>(1);
+    publicNamingContextAttrs.add(publicNamingContextAttr);
     if (showAllAttributes ||
-        (! namingContextAttr.getAttributeType().isOperational()))
+        (! publicNamingContextAttr.getAttributeType().isOperational()))
     {
-      dseUserAttrs.put(namingContextAttr.getAttributeType(),
-                       namingContextAttrs);
+      dseUserAttrs.put(publicNamingContextAttr.getAttributeType(),
+                       publicNamingContextAttrs);
     }
     else
     {
-      dseOperationalAttrs.put(namingContextAttr.getAttributeType(),
-                              namingContextAttrs);
+      dseOperationalAttrs.put(publicNamingContextAttr.getAttributeType(),
+                              publicNamingContextAttrs);
+    }
+
+
+    // Add the "ds-private-naming-contexts" attribute.
+    Attribute privateNamingContextAttr =
+         createDNAttribute(ATTR_PRIVATE_NAMING_CONTEXTS,
+                           ATTR_PRIVATE_NAMING_CONTEXTS,
+                           DirectoryServer.getPrivateNamingContexts().keySet());
+    ArrayList<Attribute> privateNamingContextAttrs =
+         new ArrayList<Attribute>(1);
+    privateNamingContextAttrs.add(privateNamingContextAttr);
+    if (showAllAttributes ||
+        (! privateNamingContextAttr.getAttributeType().isOperational()))
+    {
+      dseUserAttrs.put(privateNamingContextAttr.getAttributeType(),
+                       privateNamingContextAttrs);
+    }
+    else
+    {
+      dseOperationalAttrs.put(privateNamingContextAttr.getAttributeType(),
+                              privateNamingContextAttrs);
     }
 
 
@@ -858,7 +879,7 @@
     Map<DN,Backend> baseMap;
     if (subordinateBaseDNs == null)
     {
-      baseMap = DirectoryServer.getSuffixes();
+      baseMap = DirectoryServer.getPublicNamingContexts();
     }
     else
     {
@@ -1043,7 +1064,7 @@
         Map<DN,Backend> baseMap;
         if (subordinateBaseDNs == null)
         {
-          baseMap = DirectoryServer.getSuffixes();
+          baseMap = DirectoryServer.getPublicNamingContexts();
         }
         else
         {
@@ -1072,7 +1093,7 @@
       case SUBORDINATE_SUBTREE:
         if (subordinateBaseDNs == null)
         {
-          baseMap = DirectoryServer.getSuffixes();
+          baseMap = DirectoryServer.getPublicNamingContexts();
         }
         else
         {
@@ -1158,26 +1179,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param  controlOID  The OID of the control for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl",
-                      String.valueOf(controlOID));
-
-    // This backend does not provide any special control support.
-    return false;
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the features that may be supported by this backend.
    *
    * @return  The OIDs of the features that may be supported by this backend.
@@ -1192,26 +1193,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param  featureOID  The OID of the feature for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature",
-                      String.valueOf(featureOID));
-
-    // This backend does not provide any special feature support.
-    return false;
-  }
-
-
-
-  /**
    * Indicates whether this backend provides a mechanism to export the data it
    * contains to an LDIF file.
    *
diff --git a/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opends/src/server/org/opends/server/backends/SchemaBackend.java
index ac62d24..1bf567d 100644
--- a/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -251,11 +251,22 @@
     // Register each of the suffixes with the Directory Server.  Also, register
     // the first one as the schema base.
     this.baseDNs = baseDNs;
-    DirectoryServer.registerPrivateSuffix(baseDNs[0], this);
     DirectoryServer.setSchemaDN(baseDNs[0]);
-    for (int i=1; i < baseDNs.length; i++)
+    for (int i=0; i < baseDNs.length; i++)
     {
-      DirectoryServer.registerPrivateSuffix(baseDNs[i], this);
+      try
+      {
+        DirectoryServer.registerBaseDN(baseDNs[i], this, true, false);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "initializeBackend", e);
+
+        msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+        String message = getMessage(msgID, baseDNs[i].toString(),
+                                    stackTraceToSingleLineString(e));
+        throw new InitializationException(msgID, message, e);
+      }
     }
 
 
@@ -306,6 +317,18 @@
     assert debugEnter(CLASS_NAME, "finalizeBackend");
 
     DirectoryServer.deregisterConfigurableComponent(this);
+
+    for (DN baseDN : baseDNs)
+    {
+      try
+      {
+        DirectoryServer.deregisterBaseDN(baseDN, false);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "finalizeBackend", e);
+      }
+    }
   }
 
 
@@ -801,26 +824,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param  controlOID  The OID of the control for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl",
-                      String.valueOf(controlOID));
-
-    // This backend does not provide any special control support.
-    return false;
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the features that may be supported by this backend.
    *
    * @return  The OIDs of the features that may be supported by this backend.
@@ -835,26 +838,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param  featureOID  The OID of the feature for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature",
-                      String.valueOf(featureOID));
-
-    // This backend does not provide any special feature support.
-    return false;
-  }
-
-
-
-  /**
    * Indicates whether this backend provides a mechanism to export the data it
    * contains to an LDIF file.
    *
@@ -2227,7 +2210,7 @@
       {
         try
         {
-          DirectoryServer.deregisterSuffix(dn);
+          DirectoryServer.deregisterBaseDN(dn, false);
           if (detailedResults)
           {
             msgID = MSGID_SCHEMA_DEREGISTERED_BASE_DN;
@@ -2250,7 +2233,7 @@
       {
         try
         {
-          DirectoryServer.registerPrivateSuffix(dn, this);
+          DirectoryServer.registerBaseDN(dn, this, true, false);
           if (detailedResults)
           {
             msgID = MSGID_SCHEMA_REGISTERED_BASE_DN;
diff --git a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index a1b3094..98742e2 100644
--- a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -63,6 +63,7 @@
 import org.opends.server.types.ResultCode;
 import org.opends.server.util.LDIFException;
 
+import static org.opends.server.messages.BackendMessages.*;
 import static org.opends.server.messages.MessageHandler.*;
 import static org.opends.server.messages.JebMessages.*;
 import static org.opends.server.loggers.Error.logError;
@@ -293,10 +294,21 @@
     config = new Config();
     config.initializeConfig(configEntry, baseDNs);
 
-    // FIXME: Currently assuming every base DN is also a suffix.
     for (DN dn : baseDNs)
     {
-      DirectoryServer.registerSuffix(dn, this);
+      try
+      {
+        DirectoryServer.registerBaseDN(dn, this, false, false);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "initializeBackend", e);
+
+        int    msgID   = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+        String message = getMessage(msgID, String.valueOf(dn),
+                                    String.valueOf(e));
+        throw new InitializationException(msgID, message, e);
+      }
     }
 
 /*
@@ -402,9 +414,9 @@
     {
       try
       {
-        DirectoryServer.deregisterSuffix(dn);
+        DirectoryServer.deregisterBaseDN(dn, false);
       }
-      catch (ConfigException e)
+      catch (Exception e)
       {
         assert debugException(CLASS_NAME, "finalizeBackend", e);
       }
@@ -571,23 +583,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param featureOID The OID of the feature for which to make the
-   *                   determination.
-   * @return <CODE>true</CODE> if this backend does support the requested
-   *         feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature");
-
-    return false;  //NYI
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the controls that may be supported by this backend.
    *
    * @return The OIDs of the controls that may be supported by this backend.
@@ -602,23 +597,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param controlOID The OID of the control for which to make the
-   *                   determination.
-   * @return <CODE>true</CODE> if this backend does support the requested
-   *         control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl");
-
-    return supportedControls.contains(controlOID);
-  }
-
-
-
-  /**
    * Retrieves the set of base-level DNs that may be used within this backend.
    *
    * @return The set of base-level DNs that may be used within this backend.
@@ -1365,6 +1343,8 @@
     assert debugEnter(CLASS_NAME, "applyNewConfiguration");
 
     ConfigChangeResult ccr;
+    ResultCode resultCode = ResultCode.SUCCESS;
+    ArrayList<String> messages = new ArrayList<String>();
 
     try
     {
@@ -1397,7 +1377,7 @@
           // Even though access to the entry container map is safe, there may be
           // operation threads with a handle on the entry container being
           // closed.
-          DirectoryServer.deregisterSuffix(baseDN);
+          DirectoryServer.deregisterBaseDN(baseDN, false);
           rootContainer.removeEntryContainer(baseDN);
         }
       }
@@ -1406,9 +1386,24 @@
       {
         if (!rootContainer.getBaseDNs().contains(baseDN))
         {
-          // The base DN was added.
-          rootContainer.openEntryContainer(baseDN);
-          DirectoryServer.registerSuffix(baseDN, this);
+          try
+          {
+            // The base DN was added.
+            rootContainer.openEntryContainer(baseDN);
+            DirectoryServer.registerBaseDN(baseDN, this, false, false);
+          }
+          catch (Exception e)
+          {
+            assert debugException(CLASS_NAME, "applyNewConfiguration", e);
+
+            resultCode = DirectoryServer.getServerErrorResultCode();
+
+            int msgID   = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+            messages.add(getMessage(msgID, String.valueOf(baseDN),
+                                    String.valueOf(e)));
+            ccr = new ConfigChangeResult(resultCode, false, messages);
+            return ccr;
+          }
         }
       }
 
@@ -1420,14 +1415,13 @@
     }
     catch (Exception e)
     {
-      ArrayList<String> messages = new ArrayList<String>();
       messages.add(e.getMessage());
       ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                    false, messages);
       return ccr;
     }
 
-    ccr = new ConfigChangeResult(ResultCode.SUCCESS, false);
+    ccr = new ConfigChangeResult(resultCode, false, messages);
     return ccr;
   }
 
diff --git a/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index 8e4783d..5fcbf15 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -336,7 +336,19 @@
 
 
     // Register the task base as a private suffix.
-    DirectoryServer.registerPrivateSuffix(baseDNs[0], this);
+    try
+    {
+      DirectoryServer.registerBaseDN(taskRootDN, this, true, false);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "initializeBackend", e);
+
+      msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+      String message = getMessage(msgID, taskRootDN.toString(),
+                                  stackTraceToSingleLineString(e));
+      throw new InitializationException(msgID, message, e);
+    }
   }
 
 
@@ -381,6 +393,15 @@
     {
       assert debugException(CLASS_NAME, "finalizeBackend", e);
     }
+
+    try
+    {
+      DirectoryServer.deregisterBaseDN(taskRootDN, false);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "finalizeBackend", e);
+    }
   }
 
 
@@ -971,26 +992,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param  controlOID  The OID of the control for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsControl",
-                      String.valueOf(controlOID));
-
-    // This backend does not provide any special control support.
-    return false;
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the features that may be supported by this backend.
    *
    * @return  The OIDs of the features that may be supported by this backend.
@@ -1005,26 +1006,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param  featureOID  The OID of the feature for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    assert debugEnter(CLASS_NAME, "supportsFeature",
-                      String.valueOf(featureOID));
-
-    // This backend does not provide any special feature support.
-    return false;
-  }
-
-
-
-  /**
    * Indicates whether this backend provides a mechanism to export the data it
    * contains to an LDIF file.
    *
diff --git a/opends/src/server/org/opends/server/core/AddOperation.java b/opends/src/server/org/opends/server/core/AddOperation.java
index fdc2f56..54f9e02 100644
--- a/opends/src/server/org/opends/server/core/AddOperation.java
+++ b/opends/src/server/org/opends/server/core/AddOperation.java
@@ -1018,7 +1018,7 @@
       if (parentDN == null)
       {
         // Either this entry is a suffix or doesn't belong in the directory.
-        if (DirectoryServer.isSuffix(entryDN))
+        if (DirectoryServer.isNamingContext(entryDN))
         {
           // This is fine.  This entry is one of the configured suffixes.
           parentLock = null;
diff --git a/opends/src/server/org/opends/server/core/BackendConfigManager.java b/opends/src/server/org/opends/server/core/BackendConfigManager.java
index f5cb012..32b13ed 100644
--- a/opends/src/server/org/opends/server/core/BackendConfigManager.java
+++ b/opends/src/server/org/opends/server/core/BackendConfigManager.java
@@ -48,6 +48,7 @@
 import org.opends.server.config.MultiChoiceConfigAttribute;
 import org.opends.server.config.StringConfigAttribute;
 import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
 import org.opends.server.types.ErrorLogCategory;
 import org.opends.server.types.ErrorLogSeverity;
@@ -542,7 +543,21 @@
 
 
       // Register the backend with the server.
-      DirectoryServer.registerBackend(backend);
+      try
+      {
+        DirectoryServer.registerBackend(backend);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "initializeBackendConfig", e);
+
+        msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
+        String message = getMessage(msgID, backendID,
+                                    stackTraceToSingleLineString(e));
+        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+                 message, msgID);
+        // FIXME -- Do we need to send an admin alert?
+      }
 
 
       // Put this backend in the hash so that we will be able to find it if it
@@ -711,6 +726,64 @@
         unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
         return false;
       }
+
+      // See if the backend is registered with the server.  If it is, then
+      // see what's changed and whether those changes are acceptable.
+      Backend backend = registeredBackends.get(configEntryDN);
+      if (backend != null)
+      {
+        LinkedHashSet<DN> removedDNs = new LinkedHashSet<DN>();
+        for (DN dn : backend.getBaseDNs())
+        {
+          removedDNs.add(dn);
+        }
+
+        LinkedHashSet<DN> addedDNs = new LinkedHashSet<DN>();
+        for (DN dn : baseDNAttr.pendingValues())
+        {
+          addedDNs.add(dn);
+        }
+
+        Iterator<DN> iterator = removedDNs.iterator();
+        while (iterator.hasNext())
+        {
+          DN dn = iterator.next();
+          if (addedDNs.remove(dn))
+          {
+            iterator.remove();
+          }
+        }
+
+        for (DN dn : addedDNs)
+        {
+          try
+          {
+            DirectoryServer.registerBaseDN(dn, backend, false, true);
+          }
+          catch (DirectoryException de)
+          {
+            assert debugException(CLASS_NAME, "configChangeIsAcceptable", de);
+
+            unacceptableReason.append(de.getMessage());
+            return false;
+          }
+        }
+
+        for (DN dn : removedDNs)
+        {
+          try
+          {
+            DirectoryServer.deregisterBaseDN(dn, true);
+          }
+          catch (DirectoryException de)
+          {
+            assert debugException(CLASS_NAME, "configChangeIsAcceptable", de);
+
+            unacceptableReason.append(de.getMessage());
+            return false;
+          }
+        }
+      }
     }
     catch (Exception e)
     {
@@ -1266,7 +1339,28 @@
       }
 
       // Register the backend with the server.
-      DirectoryServer.registerBackend(backend);
+      try
+      {
+        DirectoryServer.registerBackend(backend);
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "applyConfigurationChange", e);
+
+        msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
+        String message = getMessage(msgID, backendID,
+                                    stackTraceToSingleLineString(e));
+
+        resultCode = DirectoryServer.getServerErrorResultCode();
+        messages.add(message);
+
+        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+                 message, msgID);
+        // FIXME -- Do we need to send an admin alert?
+
+        return new ConfigChangeResult(resultCode, adminActionRequired,
+                                      messages);
+      }
 
 
       registeredBackends.put(backendDN, backend);
@@ -1443,6 +1537,7 @@
 
     // See if the entry contains an attribute that specifies the set of base DNs
     // for the backend.  If it does not, then skip it.
+    List<DN> baseDNs = null;
     msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
     DNConfigAttribute baseDNStub =
          new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
@@ -1458,6 +1553,10 @@
         unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
         return false;
       }
+      else
+      {
+        baseDNs = baseDNAttr.pendingValues();
+      }
     }
     catch (Exception e)
     {
@@ -1554,6 +1653,25 @@
     }
 
 
+    // Make sure that all of the base DNs are acceptable for use in the server.
+    for (DN baseDN : baseDNs)
+    {
+      try
+      {
+        DirectoryServer.registerBaseDN(baseDN, backend, false, true);
+      }
+      catch (DirectoryException de)
+      {
+        unacceptableReason.append(de.getMessage());
+        return false;
+      }
+      catch (Exception e)
+      {
+        unacceptableReason.append(stackTraceToSingleLineString(e));
+        return false;
+      }
+    }
+
 
     // If we've gotten to this point, then it is acceptable as far as we are
     // concerned.  If it is unacceptable according to the configuration for that
@@ -1960,7 +2078,29 @@
 
     // At this point, the backend should be online.  Add it as one of the
     // registered backends for this backend config manager.
-    DirectoryServer.registerBackend(backend);
+    try
+    {
+      DirectoryServer.registerBackend(backend);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "applyConfigurationAdd", e);
+
+      msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
+      String message = getMessage(msgID, backendID,
+                                  stackTraceToSingleLineString(e));
+
+      resultCode = DirectoryServer.getServerErrorResultCode();
+      messages.add(message);
+
+      logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+               message, msgID);
+      // FIXME -- Do we need to send an admin alert?
+
+      return new ConfigChangeResult(resultCode, adminActionRequired,
+                                    messages);
+    }
+
     registeredBackends.put(backendDN, backend);
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
@@ -1995,7 +2135,7 @@
     // do know about it, then that means that it is enabled and we will not
     // allow removing a backend that is enabled.
     Backend backend = registeredBackends.get(backendDN);
-    if (backendDN == null)
+    if (backend == null)
     {
       return true;
     }
@@ -2041,7 +2181,7 @@
     // See if this backend config manager has a backend registered with the
     // provided DN.  If not, then we don't care if the entry is deleted.
     Backend backend = registeredBackends.get(backendDN);
-    if (backendDN == null)
+    if (backend == null)
     {
       return new ConfigChangeResult(resultCode, adminActionRequired,
                                     messages);
@@ -2053,6 +2193,19 @@
     Backend[] subBackends = backend.getSubordinateBackends();
     if ((subBackends == null) || (subBackends.length == 0))
     {
+      registeredBackends.remove(backendDN);
+
+      try
+      {
+        backend.finalizeBackend();
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "applyConfigurationDelete", e);
+      }
+
+      DirectoryServer.deregisterBackend(backend);
+
       return new ConfigChangeResult(resultCode, adminActionRequired,
                                     messages);
     }
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index e255767..e113da4 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -34,8 +34,11 @@
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -161,6 +164,7 @@
 import static org.opends.server.util.DynamicConstants.*;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.util.Validator.*;
 
 
 
@@ -398,19 +402,6 @@
   // The key manager provider configuration manager for the Directory Server.
   private KeyManagerProviderConfigManager keyManagerProviderConfigManager;
 
-  // The set of "private" suffixes that will be used to provide server-generated
-  // data (e.g., monitor information, schema, etc.) to clients but will not be
-  // searchable by default.
-  private LinkedHashMap<DN,Backend> privateSuffixes;
-
-  // The set of backends that have been registered with the server (mapped
-  // between the normalized suffix and the backend).
-  private LinkedHashMap<DN,Backend> suffixes;
-
-  // The set of backends that have been registered with the server (mapped
-  // between their backend ID and the backend).
-  private LinkedHashMap<String,Backend> backends;
-
   // The set of connections that are currently established.
   private LinkedHashSet<ClientConnection> establishedConnections;
 
@@ -498,6 +489,18 @@
   // The thread group for all threads associated with the Directory Server.
   private ThreadGroup directoryThreadGroup;
 
+  // The set of base DNs registered with the server.
+  private TreeMap<DN,Backend> baseDNs;
+
+  // The set of private naming contexts registered with the server.
+  private TreeMap<DN,Backend> privateNamingContexts;
+
+  // The set of public naming contexts registered with the server.
+  private TreeMap<DN,Backend> publicNamingContexts;
+
+  // The set of backends registered with the server.
+  private TreeMap<String,Backend> backends;
+
   // The set of supported controls registered with the Directory Server.
   private TreeSet<String> supportedControls;
 
@@ -606,9 +609,10 @@
     directoryServer.defaultPasswordPolicy = null;
     directoryServer.monitorProviders =
          new ConcurrentHashMap<String,MonitorProvider>();
-    directoryServer.privateSuffixes = new LinkedHashMap<DN,Backend>();
-    directoryServer.suffixes = new LinkedHashMap<DN,Backend>();
-    directoryServer.backends = new LinkedHashMap<String,Backend>();
+    directoryServer.backends = new TreeMap<String,Backend>();
+    directoryServer.baseDNs = new TreeMap<DN,Backend>();
+    directoryServer.publicNamingContexts = new TreeMap<DN,Backend>();
+    directoryServer.privateNamingContexts = new TreeMap<DN,Backend>();
     directoryServer.changeNotificationListeners =
          new CopyOnWriteArrayList<ChangeNotificationListener>();
     directoryServer.persistentSearches =
@@ -5210,12 +5214,12 @@
 
   /**
    * Retrieves the set of backends that have been registered with the Directory
-   * Server.
+   * Server, as a mapping between the backend ID and the corresponding backend.
    *
    * @return  The set of backends that have been registered with the Directory
    *          Server.
    */
-  public static LinkedHashMap<String,Backend> getBackends()
+  public static Map<String,Backend> getBackends()
   {
     assert debugEnter(CLASS_NAME, "getBackends");
 
@@ -5229,7 +5233,7 @@
    *
    * @param  backendID  The backend ID of the backend to retrieve.
    *
-   * @return  The backend with the specified backend ID, or <CODE>null</CODE> if
+   * @return  The backend with the specified backend ID, or {@code null} if
    *          there is none.
    */
   public static Backend getBackend(String backendID)
@@ -5247,8 +5251,8 @@
    *
    * @param  backendID  The backend ID for which to make the determination.
    *
-   * @return  <CODE>true</CODE> if the Directory Server has a backend with the
-   *          specified backend ID, or <CODE>false</CODE> if not.
+   * @return  {@code true} if the Directory Server has a backend with the
+   *          specified backend ID, or {@code false} if not.
    */
   public static boolean hasBackend(String backendID)
   {
@@ -5264,13 +5268,40 @@
    * will not register the set of configured suffixes with the server, as that
    * must be done by the backend itself.
    *
-   * @param  backend  The backend to register with the server.
+   * @param  backend  The backend to register with the server.  Neither the
+   *                  backend nor its backend ID may be null.
+   *
+   * @throws  DirectoryException  If the backend ID for the provided backend
+   *                              conflicts with the backend ID of an existing
+   *                              backend.
    */
   public static void registerBackend(Backend backend)
+         throws DirectoryException
   {
     assert debugEnter(CLASS_NAME, "registerBackend", String.valueOf(backend));
 
-    directoryServer.backends.put(backend.getBackendID(), backend);
+    ensureNotNull(backend);
+
+    String backendID = backend.getBackendID();
+    ensureNotNull(backendID);
+
+    synchronized (directoryServer)
+    {
+      TreeMap<String,Backend> newBackends =
+           new TreeMap<String,Backend>(directoryServer.backends);
+      if (newBackends.containsKey(backendID))
+      {
+        int    msgID   = MSGID_REGISTER_BACKEND_ALREADY_EXISTS;
+        String message = getMessage(msgID, backendID);
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+                                     msgID);
+      }
+      else
+      {
+        newBackends.put(backendID, backend);
+        directoryServer.backends = newBackends;
+      }
+    }
   }
 
 
@@ -5280,370 +5311,525 @@
    * will not deregister the set of configured suffixes with the server, as that
    * must be done by the backend itself.
    *
-   * @param  backend  the backend to deregister with the server.
+   * @param  backend  The backend to deregister with the server.  It must not be
+   *                  {@code null}.
    */
   public static void deregisterBackend(Backend backend)
   {
     assert debugEnter(CLASS_NAME, "deregisterBackend", String.valueOf(backend));
 
-    directoryServer.backends.remove(backend.getBackendID());
+    ensureNotNull(backend);
+
+    synchronized (directoryServer)
+    {
+      TreeMap<String,Backend> newBackends =
+           new TreeMap<String,Backend>(directoryServer.backends);
+      newBackends.remove(backend.getBackendID());
+
+      directoryServer.backends = newBackends;
+    }
   }
 
 
 
   /**
-   * Retrieves the set of suffixes that have been registered with the Directory
-   * Server.
+   * Retrieves the entire set of base DNs registered with the Directory Server,
+   * mapped from the base DN to the backend responsible for that base DN.  The
+   * same backend may be present multiple times, mapped from different base DNs.
    *
-   * @return  The set of suffixes that have been registered with the Directory
-   *          Server.
+   * @return  The entire set of base DNs registered with the Directory Server.
    */
-  public static LinkedHashMap<DN,Backend> getSuffixes()
+  public static Map<DN,Backend> getBaseDNs()
   {
-    assert debugEnter(CLASS_NAME, "getSuffixes");
+    assert debugEnter(CLASS_NAME, "getBaseDNs");
 
-    return directoryServer.suffixes;
+    return directoryServer.baseDNs;
   }
 
 
 
   /**
-   * Retrieves the set of "private" suffixes that have been registered with the
-   * server that will provide server-specific data to clients (e.g., monitor
-   * data, schema, etc.) but should not be considered for normal operations
-   * that may target all "user" suffixes.
+   * Retrieves the backend with the specified base DN.
    *
-   * @return  The set of "private" suffixes that have been registered with the
-   *          server.
+   * @param  baseDN  The DN that is registered as one of the base DNs for the
+   *                 backend to retrieve.
+   *
+   * @return  The backend with the specified base DN, or {@code null} if there
+   *          is no backend registered with the specified base DN.
    */
-  public static LinkedHashMap<DN,Backend> getPrivateSuffixes()
+  public static Backend getBackendWithBaseDN(DN baseDN)
   {
-    assert debugEnter(CLASS_NAME, "getPrivateSuffixes");
+    assert debugEnter(CLASS_NAME, "getBackendWithBaseDN",
+                      String.valueOf(baseDN));
 
-    return directoryServer.privateSuffixes;
+    return directoryServer.baseDNs.get(baseDN);
   }
 
 
 
   /**
-   * Indicates whether the provided DN is one of the suffixes defined in the
-   * Directory Server.
+   * Retrieves the backend that should be used to handle operations on the
+   * specified entry.
    *
-   * @param  dn  The DN for which to make the determination.
+   * @param  entryDN  The DN of the entry for which to retrieve the
+   *                  corresponding backend.
    *
-   * @return  <CODE>true</CODE> if the provided DN is one of the suffixes
-   *          defined in the Directory Server, or <CODE>false</CODE> if not.
+   * @return  The backend that should be used to handle operations on the
+   *          specified entry, or {@code null} if no appropriate backend is
+   *          registered with the server.
    */
-  public static boolean isSuffix(DN dn)
+  public static Backend getBackend(DN entryDN)
   {
-    assert debugEnter(CLASS_NAME, "isSuffix", String.valueOf(dn));
+    assert debugEnter(CLASS_NAME, "getBackendForEntry",
+                      String.valueOf(entryDN));
 
-    return (directoryServer.suffixes.containsKey(dn) ||
-            directoryServer.privateSuffixes.containsKey(dn));
-  }
-
-
-
-  /**
-   * Retrieves the backend that should be used to handle operations for the
-   * provided entry DN.
-   *
-   * @param  dn  The DN of the entry for which to retrieve the appropriate
-   *             backend.
-   *
-   * @return  The backend that should be used to handle the provided DN, or
-   *          <CODE>null</CODE> if there is no backend for the provided DN.
-   */
-  public static Backend getBackend(DN dn)
-  {
-    assert debugEnter(CLASS_NAME, "getBackend", String.valueOf(dn));
-
-    if (dn.isNullDN())
+    if (entryDN.isNullDN())
     {
       return directoryServer.rootDSEBackend;
     }
 
-    Backend backend = directoryServer.suffixes.get(dn);
-    if (backend == null)
+    TreeMap<DN,Backend> baseDNs = directoryServer.baseDNs;
+    Backend b = baseDNs.get(entryDN);
+    while (b == null)
     {
-      backend = directoryServer.privateSuffixes.get(dn);
-    }
-
-    while (backend == null)
-    {
-      dn = dn.getParentDNInSuffix();
-      if (dn == null)
+      entryDN = entryDN.getParent();
+      if (entryDN == null)
       {
-        break;
+        return null;
       }
 
-      backend = directoryServer.suffixes.get(dn);
-      if (backend == null)
-      {
-        backend = directoryServer.privateSuffixes.get(dn);
-      }
+      b = baseDNs.get(entryDN);
     }
 
-    return backend;
+    return b;
   }
 
 
 
   /**
-   * Registers the specified suffix to be handled by the provided backend.
+   * Registers the provided base DN with the server.
    *
-   * @param  suffixDN  The base DN for this suffix.
-   * @param  backend   The backend to handle operations for the provided base.
+   * @param  baseDN     The base DN to register with the server.  It must not be
+   *                    {@code null}.
+   * @param  backend    The backend responsible for the provided base DN.  It
+   *                    must not be {@code null}.
+   * @param  isPrivate  Indicates whether the base DN should be considered a
+   *                    private base DN.  If the provided base DN is a naming
+   *                    context, then this controls whether it is public or
+   *                    private.
+   * @param  testOnly   Indicates whether to only test whether the new base DN
+   *                    registration would be successful without actually
+   *                    applying any changes.
    *
-   * @throws  ConfigException  If the specified suffix is already registered
-   *                           with the Directory Server.
+   * @throws  DirectoryException  If a problem occurs while attempting to
+   *                              register the provided base DN.
    */
-  public static void registerSuffix(DN suffixDN, Backend backend)
-         throws ConfigException
+  public static void registerBaseDN(DN baseDN, Backend backend,
+                                    boolean isPrivate, boolean testOnly)
+         throws DirectoryException
   {
-    assert debugEnter(CLASS_NAME, "registerSuffix", String.valueOf(suffixDN),
-                      String.valueOf(backend));
+    assert debugEnter(CLASS_NAME, "registerBaseDN", String.valueOf(baseDN),
+                      String.valueOf(backend), String.valueOf(isPrivate),
+                      String.valueOf(testOnly));
 
-    backend.setPrivateBackend(false);
+    ensureNotNull(baseDN, backend);
 
-    synchronized (directoryServer.suffixes)
+    synchronized (directoryServer)
     {
-      // Check to see if this suffix is already in use.  It may be a suffix, or
-      // it may be a sub-suffix of an existing suffix.
-      Backend b = directoryServer.suffixes.get(suffixDN);
-      if (b != null)
-      {
-        int    msgID   = MSGID_CANNOT_REGISTER_DUPLICATE_SUFFIX;
-        String message = getMessage(msgID, String.valueOf(suffixDN),
-                                    b.getClass().getName());
+      TreeMap<DN,Backend> newBaseDNs =
+           new TreeMap<DN,Backend>(directoryServer.baseDNs);
+      TreeMap<DN,Backend> newPublicNamingContexts =
+           new TreeMap<DN,Backend>(directoryServer.publicNamingContexts);
+      TreeMap<DN,Backend> newPrivateNamingContexts =
+           new TreeMap<DN,Backend>(directoryServer.privateNamingContexts);
 
-        throw new ConfigException(msgID, message);
+
+      // Check to see if the base DN is already registered with the server.
+      Backend existingBackend = newBaseDNs.get(baseDN);
+      if (existingBackend != null)
+      {
+        int    msgID   = MSGID_REGISTER_BASEDN_ALREADY_EXISTS;
+        String message = getMessage(msgID, String.valueOf(baseDN),
+                                    backend.getBackendID(),
+                                    existingBackend.getBackendID());
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+                                     msgID);
       }
 
-      boolean found = false;
-      DN parentDN = suffixDN.getParentDNInSuffix();
+
+      // Check to see if the backend is already registered with the server for
+      // any other base DN(s).  The new base DN must not have any hierarchical
+      // relationship with any other base Dns for the same backend.
+      LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
+      for (DN dn : newBaseDNs.keySet())
+      {
+        Backend b = newBaseDNs.get(dn);
+        if (b.equals(backend))
+        {
+          otherBaseDNs.add(dn);
+
+          if (baseDN.isAncestorOf(dn) || baseDN.isDescendantOf(dn))
+          {
+            int    msgID   = MSGID_REGISTER_BASEDN_HIERARCHY_CONFLICT;
+            String message = getMessage(msgID, String.valueOf(baseDN),
+                                        backend.getBackendID(),
+                                        String.valueOf(dn));
+            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+                                         message, msgID);
+          }
+        }
+      }
+
+
+      // Check to see if the new base DN is subordinate to any other base DN
+      // already defined.  If it is, then any other base DN(s) for the same
+      // backend must also be subordinate to the same base DN.
+      Backend superiorBackend = null;
+      DN      superiorBaseDN  = null;
+      DN      parentDN        = baseDN.getParent();
       while (parentDN != null)
       {
-        b = directoryServer.suffixes.get(suffixDN);
-        if (b != null)
+        if (newBaseDNs.containsKey(parentDN))
         {
-          if (b.hasSubSuffix(suffixDN))
+          superiorBaseDN  = parentDN;
+          superiorBackend = newBaseDNs.get(parentDN);
+
+          for (DN dn : otherBaseDNs)
           {
-            int    msgID   = MSGID_CANNOT_REGISTER_DUPLICATE_SUBSUFFIX;
-            String message = getMessage(msgID, String.valueOf(suffixDN),
-                                        String.valueOf(parentDN));
-
-            throw new ConfigException(msgID, message);
+            if (! dn.isDescendantOf(superiorBaseDN))
+            {
+              int    msgID   = MSGID_REGISTER_BASEDN_DIFFERENT_PARENT_BASES;
+              String message = getMessage(msgID, String.valueOf(baseDN),
+                                          backend.getBackendID(),
+                                          String.valueOf(dn));
+              throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+                                           message, msgID);
+            }
           }
-          else
-          {
-            b.addSubordinateBackend(backend);
-            found = true;
-            break;
-          }
+
+          break;
         }
 
-        parentDN = parentDN.getParentDNInSuffix();
+        parentDN = parentDN.getParent();
       }
 
-
-      if (! found)
+      if (superiorBackend == null)
       {
-        // If we've gotten here, then it is not in use.  Register it.
-        directoryServer.suffixes.put(suffixDN, backend);
-      }
-
-
-      // See if there are any supported controls or features that we want to
-      // advertise.
-      Set<String> supportedControls = backend.getSupportedControls();
-      if (supportedControls != null)
-      {
-        for (String controlOID : supportedControls)
+        if (backend.getParentBackend() != null)
         {
-          registerSupportedControl(controlOID);
+          int    msgID   = MSGID_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE;
+          String message = getMessage(msgID, String.valueOf(baseDN),
+                                backend.getBackendID(),
+                                backend.getParentBackend().getBackendID());
+          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+                                       message, msgID);
         }
       }
 
-      Set<String> supportedFeatures = backend.getSupportedFeatures();
-      if (supportedFeatures != null)
+
+      // Check to see if the new base DN should be the superior base DN for any
+      // other base DN(s) already defined.
+      LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
+      LinkedList<DN>      subordinateBaseDNs  = new LinkedList<DN>();
+      for (DN dn : newBaseDNs.keySet())
       {
-        for (String featureOID : supportedFeatures)
-        {
-          registerSupportedFeature(featureOID);
-        }
-      }
-    }
-  }
-
-
-
-  /**
-   * Registers the specified private suffix to be handled by the provided
-   * backend.
-   *
-   * @param  suffixDN  The base DN for this suffix.
-   * @param  backend   The backend to handle operations for the provided base.
-   *
-   * @throws  ConfigException  If the specified suffix is already registered
-   *                           with the Directory Server.
-   */
-  public static void registerPrivateSuffix(DN suffixDN, Backend backend)
-         throws ConfigException
-  {
-    assert debugEnter(CLASS_NAME, "registerPrivateSuffix",
-                      String.valueOf(suffixDN), String.valueOf(backend));
-
-    backend.setPrivateBackend(true);
-
-    synchronized (directoryServer.privateSuffixes)
-    {
-      // Check to see if this suffix is already in use for a "user" suffix.  It
-      // may be a suffix, or it may be a sub-suffix of an  existing suffix.
-      synchronized (directoryServer.suffixes)
-      {
-        Backend b = directoryServer.suffixes.get(suffixDN);
-        if (b != null)
-        {
-          int    msgID   = MSGID_CANNOT_REGISTER_DUPLICATE_SUFFIX;
-          String message = getMessage(msgID, String.valueOf(suffixDN),
-                                      b.getClass().getName());
-
-          throw new ConfigException(msgID, message);
-        }
-
-        DN parentDN = suffixDN.getParentDNInSuffix();
+        Backend b = newBaseDNs.get(dn);
+        parentDN = dn.getParent();
         while (parentDN != null)
         {
-          b = directoryServer.suffixes.get(suffixDN);
-          if (b != null)
+          if (parentDN.equals(baseDN))
           {
-            int msgID = MSGID_CANNOT_REGISTER_PRIVATE_SUFFIX_BELOW_USER_PARENT;
-            String message = getMessage(msgID, String.valueOf(suffixDN),
-                                        String.valueOf(parentDN));
-            throw new ConfigException(msgID, message);
+            subordinateBaseDNs.add(dn);
+            subordinateBackends.add(b);
+            break;
+          }
+          else if (newBaseDNs.containsKey(parentDN))
+          {
+            break;
           }
 
-          parentDN = suffixDN.getParentDNInSuffix();
+          parentDN = parentDN.getParent();
         }
       }
 
 
-      // Check to see if this suffix is already registered as a private suffix
-      // or sub-suffix.
-      Backend b = directoryServer.privateSuffixes.get(suffixDN);
-      if (b != null)
+      // If we've gotten here, then the new base DN is acceptable.  If we should
+      // actually apply the changes then do so now.
+      if (! testOnly)
       {
-        int    msgID   = MSGID_CANNOT_REGISTER_DUPLICATE_SUFFIX;
-        String message = getMessage(msgID, String.valueOf(suffixDN),
-                                    b.getClass().getName());
-
-        throw new ConfigException(msgID, message);
-      }
-
-      DN parentDN = suffixDN.getParentDNInSuffix();
-      while (parentDN != null)
-      {
-        b = directoryServer.privateSuffixes.get(suffixDN);
-        if (b != null)
+        // Check to see if any of the registered backends already contain an
+        // entry with the DN specified as the base DN.  This could happen if
+        // we're creating a new subordinate backend in an existing directory
+        // (e.g., moving the "ou=People,dc=example,dc=com" branch to its own
+        // backend when that data already exists under the "dc=example,dc=com"
+        // backend).  This condition shouldn't prevent the new base DN from
+        // being registered, but it's definitely important enough that we let
+        // the administrator know about it and remind them that the existing
+        // backend will need to be reinitialized.
+        if (superiorBackend != null)
         {
-          if (b.hasSubSuffix(suffixDN))
+          if (superiorBackend.entryExists(baseDN))
           {
-            int    msgID   = MSGID_CANNOT_REGISTER_DUPLICATE_SUBSUFFIX;
-            String message = getMessage(msgID, String.valueOf(suffixDN),
-                                        String.valueOf(parentDN));
+            int    msgID   = MSGID_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS;
+            String message = getMessage(msgID, superiorBackend.getBackendID(),
+                                        String.valueOf(baseDN),
+                                        backend.getBackendID());
+            logError(ErrorLogCategory.CONFIGURATION,
+                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+          }
+        }
 
-            throw new ConfigException(msgID, message);
+
+        newBaseDNs.put(baseDN, backend);
+
+        if (superiorBackend == null)
+        {
+          if (isPrivate)
+          {
+            backend.setPrivateBackend(true);
+            newPrivateNamingContexts.put(baseDN, backend);
           }
           else
           {
-            b.addSubordinateBackend(backend);
-            return;
+            backend.setPrivateBackend(false);
+            newPublicNamingContexts.put(baseDN, backend);
           }
         }
+        else if (otherBaseDNs.isEmpty())
+        {
+          backend.setParentBackend(superiorBackend);
+          superiorBackend.addSubordinateBackend(backend);
+        }
 
-        parentDN = suffixDN.getParentDNInSuffix();
+        for (Backend b : subordinateBackends)
+        {
+          Backend oldParentBackend = b.getParentBackend();
+          if (oldParentBackend != null)
+          {
+            oldParentBackend.removeSubordinateBackend(b);
+          }
+
+          b.setParentBackend(backend);
+          backend.addSubordinateBackend(b);
+        }
+
+        for (DN dn : subordinateBaseDNs)
+        {
+          newPublicNamingContexts.remove(dn);
+          newPrivateNamingContexts.remove(dn);
+        }
+
+        directoryServer.baseDNs               = newBaseDNs;
+        directoryServer.publicNamingContexts  = newPublicNamingContexts;
+        directoryServer.privateNamingContexts = newPrivateNamingContexts;
       }
-
-
-      // If we've gotten here, then it is not in use.  Register it.
-      directoryServer.privateSuffixes.put(suffixDN, backend);
     }
   }
 
 
 
   /**
-   * Deregisters the specified suffix with the Directory Server.  This should
-   * work regardless of whether the specified DN is a normal suffix or a private
-   * suffix.
+   * Deregisters the provided base DN with the server.
    *
-   * @param  suffixDN  The suffix DN to deregister with the server.
+   * @param  baseDN     The base DN to deregister with the server.  It must not
+   *                    be {@code null}.
+   * @param  testOnly   Indicates whether to only test whether the new base DN
+   *                    registration would be successful without actually
+   *                    applying any changes.
    *
-   * @throws  ConfigException  If a problem occurs while attempting to
-   *                           deregister the specified suffix.
+   * @throws  DirectoryException  If a problem occurs while attempting to
+   *                              deregister the provided base DN.
    */
-  public static void deregisterSuffix(DN suffixDN)
-         throws ConfigException
+  public static void deregisterBaseDN(DN baseDN, boolean testOnly)
+         throws DirectoryException
   {
-    assert debugEnter(CLASS_NAME, "deregisterSuffix", String.valueOf(suffixDN));
+    assert debugEnter(CLASS_NAME, "deregisterBaseDN", String.valueOf(baseDN));
 
+    ensureNotNull(baseDN);
 
-    // First, check to see if it is a "user" suffix or sub-suffix.
-    synchronized (directoryServer.suffixes)
+    synchronized (directoryServer)
     {
-      Backend b = directoryServer.suffixes.remove(suffixDN);
-      if (b != null)
+      TreeMap<DN,Backend> newBaseDNs =
+           new TreeMap<DN,Backend>(directoryServer.baseDNs);
+      TreeMap<DN,Backend> newPublicNamingContexts =
+           new TreeMap<DN,Backend>(directoryServer.publicNamingContexts);
+      TreeMap<DN,Backend> newPrivateNamingContexts =
+           new TreeMap<DN,Backend>(directoryServer.privateNamingContexts);
+
+
+      // Make sure that the Directory Server actually contains a backend with
+      // the specified base DN.
+      Backend backend = newBaseDNs.get(baseDN);
+      if (backend == null)
       {
-        return;
+        int    msgID   = MSGID_DEREGISTER_BASEDN_NOT_REGISTERED;
+        String message = getMessage(msgID, String.valueOf(baseDN));
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+                                     msgID);
       }
 
-      DN parentDN = suffixDN.getParentDNInSuffix();
-      while (parentDN != null)
-      {
-        b = directoryServer.suffixes.get(parentDN);
-        if (b != null)
-        {
-          if (b.hasSubSuffix(suffixDN))
-          {
-            b.removeSubSuffix(suffixDN, parentDN);
-          }
 
-          return;
+      // Check to see if the backend has a parent backend, and whether it has
+      // any subordinates with base DNs that are below the base DN to remove.
+      Backend             superiorBackend     = backend.getParentBackend();
+      LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
+      if (backend.getSubordinateBackends() != null)
+      {
+        for (Backend b : backend.getSubordinateBackends())
+        {
+          for (DN dn : b.getBaseDNs())
+          {
+            if (dn.isDescendantOf(baseDN))
+            {
+              subordinateBackends.add(b);
+              break;
+            }
+          }
         }
       }
-    }
 
 
-    // Check the set of private suffixes and sub-suffixes.
-    synchronized (directoryServer.privateSuffixes)
-    {
-      Backend b = directoryServer.privateSuffixes.remove(suffixDN);
-      if (b != null)
+      // See if there are any other base DNs registered within the same backend.
+      LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
+      for (DN dn : newBaseDNs.keySet())
       {
-        return;
-      }
-
-      DN parentDN = suffixDN.getParentDNInSuffix();
-      while (parentDN != null)
-      {
-        b = directoryServer.privateSuffixes.get(parentDN);
-        if (b != null)
+        if (dn.equals(baseDN))
         {
-          if (b.hasSubSuffix(suffixDN))
-          {
-            b.removeSubSuffix(suffixDN, parentDN);
-          }
+          continue;
+        }
 
-          return;
+        Backend b = newBaseDNs.get(dn);
+        if (backend.equals(b))
+        {
+          otherBaseDNs.add(dn);
         }
       }
+
+
+      // If we've gotten here, then it's OK to make the changes.
+      if (! testOnly)
+      {
+        // Get rid of the references to this base DN in the mapping tree
+        // information.
+        newBaseDNs.remove(baseDN);
+        newPublicNamingContexts.remove(baseDN);
+        newPrivateNamingContexts.remove(baseDN);
+
+
+        if (superiorBackend == null)
+        {
+          // If there were any subordinate backends, then all of their base DNs
+          // will now be promoted to naming contexts.
+          for (Backend b : subordinateBackends)
+          {
+            b.setParentBackend(null);
+            backend.removeSubordinateBackend(b);
+
+            for (DN dn : b.getBaseDNs())
+            {
+              if (b.isPrivateBackend())
+              {
+                newPrivateNamingContexts.put(dn, b);
+              }
+              else
+              {
+                newPublicNamingContexts.put(dn, b);
+              }
+            }
+          }
+        }
+        else
+        {
+          // If there are no other base DNs for the associated backend, then
+          // remove this backend as a subordinate of the parent backend.
+          if (otherBaseDNs.isEmpty())
+          {
+            superiorBackend.removeSubordinateBackend(backend);
+          }
+
+
+          // If there are any subordinate backends, then they need to be made
+          // subordinate to the parent backend.  Also, we should log a warning
+          // message indicating that there may be inconsistent search results
+          // because some of the structural entries will be missing.
+          if (! subordinateBackends.isEmpty())
+          {
+            int    msgID   = MSGID_DEREGISTER_BASEDN_MISSING_HIERARCHY;
+            String message = getMessage(msgID, String.valueOf(baseDN),
+                                        backend.getBackendID());
+            logError(ErrorLogCategory.CONFIGURATION,
+                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+
+            for (Backend b : subordinateBackends)
+            {
+              backend.removeSubordinateBackend(b);
+              superiorBackend.addSubordinateBackend(b);
+              b.setParentBackend(superiorBackend);
+            }
+          }
+        }
+
+
+        directoryServer.baseDNs               = newBaseDNs;
+        directoryServer.publicNamingContexts  = newPublicNamingContexts;
+        directoryServer.privateNamingContexts = newPrivateNamingContexts;
+      }
     }
   }
 
 
 
   /**
+   * Retrieves the set of public naming contexts defined in the Directory
+   * Server, mapped from the naming context DN to the corresponding backend.
+   *
+   * @return  The set of public naming contexts defined in the Directory Server.
+   */
+  public static Map<DN,Backend> getPublicNamingContexts()
+  {
+    assert debugEnter(CLASS_NAME, "getPublicNamingContexts");
+
+    return directoryServer.publicNamingContexts;
+  }
+
+
+
+  /**
+   * Retrieves the set of private naming contexts defined in the Directory
+   * Server, mapped from the naming context DN to the corresponding backend.
+   *
+   * @return  The set of private naming contexts defined in the Directory
+   *          Server.
+   */
+  public static Map<DN,Backend> getPrivateNamingContexts()
+  {
+    assert debugEnter(CLASS_NAME, "getPrivateNamingContexts");
+
+    return directoryServer.privateNamingContexts;
+  }
+
+
+
+  /**
+   * Indicates whether the specified DN is one of the Directory Server naming
+   * contexts.
+   *
+   * @param  dn  The DN for which to make the determination.
+   *
+   * @return  {@code true} if the specified DN is a naming context for the
+   *          Directory Server, or {@code false} if it is not.
+   */
+  public static boolean isNamingContext(DN dn)
+  {
+    assert debugEnter(CLASS_NAME, "isNamingContext");
+
+    return (directoryServer.publicNamingContexts.containsKey(dn) ||
+            directoryServer.privateNamingContexts.containsKey(dn));
+  }
+
+
+
+  /**
    * Retrieves the root DSE entry for the Directory Server.
    *
    * @return  The root DSE entry for the Directory Server.
diff --git a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 9e045a1..f377127 100644
--- a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -598,7 +598,8 @@
 
     try
     {
-      DirectoryServer.registerPrivateSuffix(configRootEntry.getDN(), this);
+      DirectoryServer.registerBaseDN(configRootEntry.getDN(), this, true,
+                                     false);
     }
     catch (Exception e)
     {
@@ -729,7 +730,7 @@
 
     try
     {
-      DirectoryServer.deregisterSuffix(configRootEntry.getDN());
+      DirectoryServer.deregisterBaseDN(configRootEntry.getDN(), false);
     }
     catch (Exception e)
     {
@@ -1796,23 +1797,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified control.
-   *
-   * @param  controlOID  The OID of the control for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          control, or <CODE>false</CODE>
-   */
-  public boolean supportsControl(String controlOID)
-  {
-    // NYI
-    return false;
-  }
-
-
-
-  /**
    * Retrieves the OIDs of the features that may be supported by this backend.
    *
    * @return  The OIDs of the features that may be supported by this backend.
@@ -1826,23 +1810,6 @@
 
 
   /**
-   * Indicates whether this backend supports the specified feature.
-   *
-   * @param  featureOID  The OID of the feature for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if this backend does support the requested
-   *          feature, or <CODE>false</CODE>
-   */
-  public boolean supportsFeature(String featureOID)
-  {
-    // NYI
-    return false;
-  }
-
-
-
-  /**
    * Indicates whether this backend provides a mechanism to export the data it
    * contains to an LDIF file.
    *
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index db68ea0..f3d3c70 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -35,9 +35,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -309,7 +309,7 @@
       // use it.  Otherwise, add a realm for each suffix defined in the server.
       if (realm == null)
       {
-        LinkedHashMap<DN,Backend> suffixes = DirectoryServer.getSuffixes();
+        Map<DN,Backend> suffixes = DirectoryServer.getPublicNamingContexts();
         if (! suffixes.isEmpty())
         {
           Iterator<DN> iterator = suffixes.keySet().iterator();
diff --git a/opends/src/server/org/opends/server/messages/BackendMessages.java b/opends/src/server/org/opends/server/messages/BackendMessages.java
index d1ecddb..3691cce 100644
--- a/opends/src/server/org/opends/server/messages/BackendMessages.java
+++ b/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -2233,6 +2233,17 @@
 
 
   /**
+   * The message ID for the message that will be used if an error occurs while
+   * attempting to register a base DN for use in the server.  This takes two
+   * arguments, which are the base DN and a string representation of the
+   * exception that was caught.
+   */
+  public static final int MSGID_BACKEND_CANNOT_REGISTER_BASEDN =
+       CATEGORY_MASK_BACKEND | SEVERITY_MASK_FATAL_ERROR | 210;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
    */
@@ -2252,6 +2263,9 @@
                     "the entry is already locked by a long-running operation " +
                     "or that the entry has previously been locked but was " +
                     "not properly unlocked.");
+    registerMessage(MSGID_BACKEND_CANNOT_REGISTER_BASEDN,
+                    "An error occurred while attempting to register base DN " +
+                    "in the Directory Server:  %s.");
 
 
     registerMessage(MSGID_ROOTDSE_CONFIG_ENTRY_NULL,
diff --git a/opends/src/server/org/opends/server/messages/ConfigMessages.java b/opends/src/server/org/opends/server/messages/ConfigMessages.java
index 237e237..744d98b 100644
--- a/opends/src/server/org/opends/server/messages/ConfigMessages.java
+++ b/opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -6153,6 +6153,17 @@
 
 
   /**
+   * The message ID for the message that will be used if an error occurs while
+   * attempting to register a backend with the Directory Server.  This takes two
+   * arguments, which are the backend ID and a string representation of the
+   * exception that was caught.
+   */
+  public static final int MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND =
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 572;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
    */
@@ -7020,6 +7031,9 @@
                     "lock for backend %s:  %s.  This may interfere with " +
                     "operations that require exclusive access, including " +
                     "LDIF import and restoring a backup.");
+    registerMessage(MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND,
+                    "An error occurred while attempting to register backend " +
+                    "%s with the Directory Server:  %s.");
     registerMessage(MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND,
                     "The class %s specified in configuration entry %s does " +
                     "not contain a valid Directory Server backend " +
diff --git a/opends/src/server/org/opends/server/messages/CoreMessages.java b/opends/src/server/org/opends/server/messages/CoreMessages.java
index b497d0a..8d29a84 100644
--- a/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5967,6 +5967,98 @@
 
 
   /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a backend with an ID that is already registered.  This takes a
+   * single argument, which is the conflicting backend ID.
+   */
+  public static final int MSGID_REGISTER_BACKEND_ALREADY_EXISTS =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 571;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a base DN that is already registered.  This takes three arguments,
+   * which are the base DN, the backend ID for the new backend, and the backend
+   * ID for the already-registered backend.
+   */
+  public static final int MSGID_REGISTER_BASEDN_ALREADY_EXISTS =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 572;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a base DN with a backend that already has another base DN that
+   * contains conflicting hierarchy.  This takes three arguments, which are the
+   * base DN to be registered, the backend ID, and the already-registered base
+   * DN.
+   */
+  public static final int MSGID_REGISTER_BASEDN_HIERARCHY_CONFLICT =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 573;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a base DN with a backend that already contains other base DNs
+   * that do not share the same parent base DN.  This takes three arguments,
+   * which are the base DN to be registered, the backend ID, and the
+   * already-registered base DN.
+   */
+  public static final int MSGID_REGISTER_BASEDN_DIFFERENT_PARENT_BASES =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 574;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a base DN with a backend that already contains other base DNs
+   * that are subordinate to a backend whereas the new base DN is not
+   * subordinate to any backend.  This takes three arguments, which are the base
+   * DN to be registered, the backend ID, and the backend ID of the parent
+   * backend.
+   */
+  public static final int MSGID_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 575;
+
+
+
+  /**
+   * The message ID for the message that will be used if a newly-registered
+   * backend has a base DN for which a corresponding entry already exists in the
+   * server.  This takes three arguments, which are the backend ID for the
+   * existing backend, the base DN for the new backend, and the backend ID for
+   * the new backend.
+   */
+  public static final int MSGID_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 576;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * deregister a base DN that is not registered.  This takes a single argument,
+   * which is the base DN to deregister.
+   */
+  public static final int MSGID_DEREGISTER_BASEDN_NOT_REGISTERED =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 577;
+
+
+
+  /**
+   * The message ID for the message that will be used if a base DN containing
+   * both superior and subordinate backends is deregistered, leaving the
+   * possibility for missing entries in the data hierarchy.  This takes two
+   * arguments, which are the base DN that has been deregistered and the backend
+   * ID of the associated backend.
+   */
+  public static final int MSGID_DEREGISTER_BASEDN_MISSING_HIERARCHY =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 578;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined
    * in this class.
    */
@@ -8057,6 +8149,50 @@
                     "The user-specific lookthrough limit value %s contained " +
                     "in user entry %s could not be parsed as an integer.  " +
                     "The default server lookthrough limit will be used.");
+
+
+    registerMessage(MSGID_REGISTER_BACKEND_ALREADY_EXISTS,
+                    "Unable to register backend %s with the Directory Server " +
+                    "because another backend with the same backend ID is " +
+                    "already registered.");
+    registerMessage(MSGID_REGISTER_BASEDN_ALREADY_EXISTS,
+                    "Unable to register base DN %s with the Directory Server " +
+                    "for backend %s because that base DN is already " +
+                    "registered for backend %s.");
+    registerMessage(MSGID_REGISTER_BASEDN_HIERARCHY_CONFLICT,
+                    "Unable to register base DN %s with the Directory Server " +
+                    "for backend %s because that backend already contains " +
+                    "another base DN %s that is within the same hierarchical " +
+                    "path.");
+    registerMessage(MSGID_REGISTER_BASEDN_DIFFERENT_PARENT_BASES,
+                    "Unable to register base DN %s with the Directory Server " +
+                    "for backend %s because that backend already contains " +
+                    "another base DN %s that is not subordinate to the same " +
+                    "base DN in the parent backend.");
+    registerMessage(MSGID_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE,
+                    "Unable to register base DN %s with the Directory Server " +
+                    "for backend %s because that backend already contains " +
+                    "one or more other base DNs that are subordinate to " +
+                    "backend %s but the new base DN is not.");
+    registerMessage(MSGID_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS,
+                    "Backend %s already contains entry %s which has just " +
+                    "been registered as the base DN for backend %s.  " +
+                    "These conflicting entries may cause unexpected or " +
+                    "errant search results, and both backends should be " +
+                    "reinitialized to ensure that each has the correct " +
+                    "content.");
+    registerMessage(MSGID_DEREGISTER_BASEDN_NOT_REGISTERED,
+                    "Unable to de-register base DN %s with the Directory " +
+                    "Server because that base DN is not registered for any " +
+                    "active backend.");
+    registerMessage(MSGID_DEREGISTER_BASEDN_MISSING_HIERARCHY,
+                    "Base DN %s has been deregistered from the Directory " +
+                    "Server for backend %s.  This base DN had both superior " +
+                    "and subordinate entries in other backends, and there " +
+                    "may be inconsistent or unexpected behavior when " +
+                    "accessing entries in this portion of the hierarchy " +
+                    "because of the missing entries that had been held in " +
+                    "the de-registered backend.");
   }
 }
 
diff --git a/opends/src/server/org/opends/server/types/DN.java b/opends/src/server/org/opends/server/types/DN.java
index 45de88c..242e0cd 100644
--- a/opends/src/server/org/opends/server/types/DN.java
+++ b/opends/src/server/org/opends/server/types/DN.java
@@ -507,7 +507,9 @@
   public DN getParentDNInSuffix() {
     assert debugEnter(CLASS_NAME, "getParentDNInSuffix");
 
-    if ((numComponents <= 1) || (DirectoryServer.isSuffix(this))) {
+    if ((numComponents <= 1) ||
+        DirectoryServer.isNamingContext(this))
+    {
       return null;
     }
 
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index 4190996..34bf7c6 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -186,8 +186,8 @@
 
   /**
    * The name of the standard attribute that is used to specify the set of
-   * naming contexts (suffixes) for the Directory Server, formatted in camel
-   * case.
+   * public naming contexts (suffixes) for the Directory Server, formatted in
+   * camel case.
    */
   public static final String ATTR_NAMING_CONTEXTS = "namingContexts";
 
@@ -195,14 +195,23 @@
 
   /**
    * The name of the standard attribute that is used to specify the set of
-   * naming contexts (suffixes) for the Directory Server, formatted in all
-   * lowercase.
+   * public naming contexts (suffixes) for the Directory Server, formatted in
+   * all lowercase.
    */
   public static final String ATTR_NAMING_CONTEXTS_LC = "namingcontexts";
 
 
 
   /**
+   * The name of the attribute used to hold the DNs that constitute the set of
+   * "private" naming contexts registered with the server.
+   */
+  public static final String ATTR_PRIVATE_NAMING_CONTEXTS =
+       "ds-private-naming-contexts";
+
+
+
+  /**
    * The name of the standard attribute that is used to hold organization names,
    * formatted in all lowercase.
    */
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
index e4b4266..b3b884c 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -76,7 +76,7 @@
    */
   public static final String PROPERTY_LDAP_PORT =
        "org.opends.server.LdapPort";
-  
+
   /**
    * The string representation of the DN that will be used as the base entry for
    * the test backend.  This must not be changed, as there are a number of test
@@ -151,7 +151,7 @@
     testRoot.mkdirs();
     //db_verify is second jeb backend used by the jeb verify test cases
     String[] subDirectories = { "bak", "bin", "changelogDb", "classes",
-                                "config", "db", "db_verify", "ldif", "lib", 
+                                "config", "db", "db_verify", "ldif", "lib",
                                 "locks", "logs" };
     for (String s : subDirectories)
     {
@@ -348,6 +348,7 @@
     if (memoryBackend == null)
     {
       memoryBackend = new MemoryBackend();
+      memoryBackend.setBackendID("test");
       memoryBackend.initializeBackend(null, new DN[] { baseDN });
       DirectoryServer.registerBackend(memoryBackend);
     }
@@ -367,12 +368,12 @@
 
    * @param  createBaseEntry  Indicate whether to automatically create the base
    *                          entry and add it to the backend.
-   *                          
+   *
    * @param beID  The be id to clear.
-   * 
+   *
    * @param dn   The suffix of the backend to create if the the createBaseEntry
    *             boolean is true.
-   *             
+   *
    * @throws  Exception  If an unexpected problem occurs.
    */
   public static void clearJEBackend(boolean createBaseEntry, String beID, String dn)
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
new file mode 100644
index 0000000..66e4204
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
@@ -0,0 +1,783 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.util.ArrayList;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.api.Backend;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * A set of generic test cases that cover adding, modifying, and removing
+ * Directory Server backends.
+ */
+public class BackendConfigManagerTestCase
+       extends CoreTestCase
+{
+  /**
+   * Ensures that the Directory Server is running.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @BeforeClass()
+  public void startServer()
+         throws Exception
+  {
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
+   * Tests that the server will reject an attempt to register a base DN that is
+   * already defined in the server.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testRegisterBaseThatAlreadyExists()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(false);
+
+    DN baseDN = DN.decode("o=test");
+    String backendID = createBackendID(baseDN);
+    Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+                         backendEntry.getUserAttributes(),
+                         backendEntry.getOperationalAttributes());
+    assertFalse(addOperation.getResultCode() == ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests that the server will reject an attempt to deregister a base DN that
+   * is not defined in the server.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(expectedExceptions = { DirectoryException.class })
+  public void testDeregisterNonExistentBaseDN()
+         throws Exception
+  {
+    DirectoryServer.deregisterBaseDN(DN.decode("o=unregistered"), false);
+  }
+
+
+
+  /**
+   * Tests that the server will reject an attempt to register a base DN using a
+   * backend with a backend ID that is already defined in the server.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testRegisterBackendIDThatAlreadyExists()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(false);
+
+    DN baseDN = DN.decode("o=test");
+    String backendID = "test";
+    Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+                         backendEntry.getUserAttributes(),
+                         backendEntry.getOperationalAttributes());
+    assertFalse(addOperation.getResultCode() == ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests the ability of the server to create and remove a backend that is
+   * never enabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddAndRemoveDisabledBackend()
+         throws Exception
+  {
+    DN baseDN = DN.decode("o=bcmtest");
+    String backendID = createBackendID(baseDN);
+    Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+                         backendEntry.getUserAttributes(),
+                         backendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(backendID));
+    assertNull(DirectoryServer.getBackendWithBaseDN(baseDN));
+
+    DeleteOperation deleteOperation = conn.processDelete(backendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests the ability of the server to create and remove a backend that is
+   * enabled.  It will also test the ability of that backend to hold entries.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddAndRemoveEnabledBackend()
+         throws Exception
+  {
+    DN baseDN = DN.decode("o=bcmtest");
+    String backendID = createBackendID(baseDN);
+    Entry backendEntry = createBackendEntry(backendID, true, baseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+                         backendEntry.getUserAttributes(),
+                         backendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend backend = DirectoryServer.getBackend(backendID);
+    assertNotNull(backend);
+    assertEquals(backend, DirectoryServer.getBackendWithBaseDN(baseDN));
+    assertNull(backend.getParentBackend());
+    assertTrue(backend.getSubordinateBackends().length == 0);
+    assertFalse(backend.entryExists(baseDN));
+    assertTrue(DirectoryServer.isNamingContext(baseDN));
+
+    Entry e = createEntry(baseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(backend.entryExists(baseDN));
+
+    DeleteOperation deleteOperation = conn.processDelete(backendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(backendID));
+  }
+
+
+
+  /**
+   * Tests the ability of the server to create a backend that is disabled and
+   * then enable it through a configuration change, and then subsequently
+   * disable it.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testEnableAndDisableBackend()
+         throws Exception
+  {
+    // Create the backend and make it disabled.
+    DN baseDN = DN.decode("o=bcmtest");
+    String backendID = createBackendID(baseDN);
+    Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+                         backendEntry.getUserAttributes(),
+                         backendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(backendID));
+    assertFalse(DirectoryServer.isNamingContext(baseDN));
+
+
+    // Modify the backend to enable it.
+    ArrayList<Modification> mods = new ArrayList<Modification>();
+    mods.add(new Modification(ModificationType.REPLACE,
+                              new Attribute("ds-cfg-backend-enabled", "true")));
+    ModifyOperation modifyOperation =
+         conn.processModify(backendEntry.getDN(), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend backend = DirectoryServer.getBackend(backendID);
+    assertNotNull(backend);
+    assertEquals(backend, DirectoryServer.getBackendWithBaseDN(baseDN));
+    assertNull(backend.getParentBackend());
+    assertTrue(backend.getSubordinateBackends().length == 0);
+    assertFalse(backend.entryExists(baseDN));
+    assertTrue(DirectoryServer.isNamingContext(baseDN));
+
+    Entry e = createEntry(baseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(backend.entryExists(baseDN));
+
+
+    // Modify the backend to disable it.
+    mods = new ArrayList<Modification>();
+    mods.add(new Modification(ModificationType.REPLACE,
+                              new Attribute("ds-cfg-backend-enabled", "false")));
+    modifyOperation =
+         conn.processModify(backendEntry.getDN(), mods);
+    assertNull(DirectoryServer.getBackend(backendID));
+    assertFalse(DirectoryServer.entryExists(baseDN));
+    assertFalse(DirectoryServer.isNamingContext(baseDN));
+
+
+    // Delete the disabled backend.
+    DeleteOperation deleteOperation = conn.processDelete(backendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests the ability of the Directory Server to work properly when adding
+   * nested backends in which the parent is added first and the child second.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddNestedBackendParentFirst()
+         throws Exception
+  {
+    // Create the parent backend and the corresponding base entry.
+    DN parentBaseDN = DN.decode("o=parent");
+    String parentBackendID = createBackendID(parentBaseDN);
+    Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
+                                                  parentBaseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(parentBackendEntry.getDN(),
+                         parentBackendEntry.getObjectClasses(),
+                         parentBackendEntry.getUserAttributes(),
+                         parentBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend parentBackend = DirectoryServer.getBackend(parentBackendID);
+    assertNotNull(parentBackend);
+    assertEquals(parentBackend,
+                 DirectoryServer.getBackendWithBaseDN(parentBaseDN));
+    assertNull(parentBackend.getParentBackend());
+    assertTrue(parentBackend.getSubordinateBackends().length == 0);
+    assertFalse(parentBackend.entryExists(parentBaseDN));
+    assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
+
+    Entry e = createEntry(parentBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(parentBackend.entryExists(parentBaseDN));
+
+
+    // Create the child backend and the corresponding base entry.
+    DN childBaseDN = DN.decode("ou=child,o=parent");
+    String childBackendID = createBackendID(childBaseDN);
+    Entry childBackendEntry = createBackendEntry(childBackendID, true,
+                                                 childBaseDN);
+
+    addOperation =
+         conn.processAdd(childBackendEntry.getDN(),
+                         childBackendEntry.getObjectClasses(),
+                         childBackendEntry.getUserAttributes(),
+                         childBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend childBackend = DirectoryServer.getBackend(childBackendID);
+    assertNotNull(childBackend);
+    assertEquals(childBackend,
+                 DirectoryServer.getBackendWithBaseDN(childBaseDN));
+    assertNotNull(childBackend.getParentBackend());
+    assertEquals(parentBackend, childBackend.getParentBackend());
+    assertTrue(parentBackend.getSubordinateBackends().length == 1);
+    assertFalse(childBackend.entryExists(childBaseDN));
+    assertFalse(DirectoryServer.isNamingContext(childBaseDN));
+
+    e = createEntry(childBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(childBackend.entryExists(childBaseDN));
+
+
+    // Make sure that both entries exist.
+    InternalSearchOperation internalSearch =
+         conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+              SearchFilter.createFilterFromString("(objectClass=*)"));
+    assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+    assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+
+    // Make sure that we can't remove the parent backend with the child still
+    // in place.
+    DeleteOperation deleteOperation =
+         conn.processDelete(parentBackendEntry.getDN());
+    assertFalse(deleteOperation.getResultCode() == ResultCode.SUCCESS);
+    assertNotNull(DirectoryServer.getBackend(parentBackendID));
+
+    // Delete the child and then delete the parent.
+    deleteOperation = conn.processDelete(childBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(childBackendID));
+    assertTrue(parentBackend.getSubordinateBackends().length == 0);
+
+    deleteOperation = conn.processDelete(parentBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(parentBackendID));
+  }
+
+
+
+  /**
+   * Tests the ability of the Directory Server to work properly when adding
+   * nested backends in which the child is added first and the parent second.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddNestedBackendChildFirst()
+         throws Exception
+  {
+    // Create the child backend and the corresponding base entry (at the time
+    // of the creation, it will be a naming context).
+    DN childBaseDN = DN.decode("ou=child,o=parent");
+    String childBackendID = createBackendID(childBaseDN);
+    Entry childBackendEntry = createBackendEntry(childBackendID, true,
+                                                 childBaseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(childBackendEntry.getDN(),
+                         childBackendEntry.getObjectClasses(),
+                         childBackendEntry.getUserAttributes(),
+                         childBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend childBackend = DirectoryServer.getBackend(childBackendID);
+    assertNotNull(childBackend);
+    assertEquals(childBackend,
+                 DirectoryServer.getBackendWithBaseDN(childBaseDN));
+    assertFalse(childBackend.entryExists(childBaseDN));
+    assertNull(childBackend.getParentBackend());
+    assertTrue(childBackend.getSubordinateBackends().length == 0);
+    assertFalse(childBackend.entryExists(childBaseDN));
+
+    Entry e = createEntry(childBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(childBackend.entryExists(childBaseDN));
+    assertTrue(DirectoryServer.isNamingContext(childBaseDN));
+
+
+    // Create the parent backend and the corresponding entry (and verify that
+    // its DN is now a naming context and the child's is not).
+    DN parentBaseDN = DN.decode("o=parent");
+    String parentBackendID = createBackendID(parentBaseDN);
+    Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
+                                                  parentBaseDN);
+
+    addOperation =
+         conn.processAdd(parentBackendEntry.getDN(),
+                         parentBackendEntry.getObjectClasses(),
+                         parentBackendEntry.getUserAttributes(),
+                         parentBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend parentBackend = DirectoryServer.getBackend(parentBackendID);
+    assertNotNull(parentBackend);
+    assertEquals(parentBackend,
+                 DirectoryServer.getBackendWithBaseDN(parentBaseDN));
+    assertNotNull(childBackend.getParentBackend());
+    assertEquals(parentBackend, childBackend.getParentBackend());
+    assertTrue(parentBackend.getSubordinateBackends().length == 1);
+
+    e = createEntry(parentBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(parentBackend.entryExists(parentBaseDN));
+    assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
+    assertFalse(DirectoryServer.isNamingContext(childBaseDN));
+
+
+    // Verify that we can see both entries with a subtree search.
+    InternalSearchOperation internalSearch =
+         conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+              SearchFilter.createFilterFromString("(objectClass=*)"));
+    assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+    assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+    // Delete the backends from the server.
+    DeleteOperation deleteOperation =
+         conn.processDelete(childBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(childBackendID));
+    assertTrue(parentBackend.getSubordinateBackends().length == 0);
+
+    deleteOperation = conn.processDelete(parentBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(parentBackendID));
+  }
+
+
+
+  /**
+   * Tests the ability of the Directory Server to work properly when inserting
+   * an intermediate backend between a parent backend and an existing nested
+   * backend.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testInsertIntermediateBackend()
+         throws Exception
+  {
+    // Add the parent backend to the server and its corresponding base entry.
+    DN parentBaseDN = DN.decode("o=parent");
+    String parentBackendID = createBackendID(parentBaseDN);
+    Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
+                                                  parentBaseDN);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(parentBackendEntry.getDN(),
+                         parentBackendEntry.getObjectClasses(),
+                         parentBackendEntry.getUserAttributes(),
+                         parentBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend parentBackend = DirectoryServer.getBackend(parentBackendID);
+    assertNotNull(parentBackend);
+    assertEquals(parentBackend,
+                 DirectoryServer.getBackendWithBaseDN(parentBaseDN));
+    assertNull(parentBackend.getParentBackend());
+    assertTrue(parentBackend.getSubordinateBackends().length == 0);
+    assertFalse(parentBackend.entryExists(parentBaseDN));
+
+    Entry e = createEntry(parentBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(parentBackend.entryExists(parentBaseDN));
+    assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
+
+
+    // Add the grandchild backend to the server.
+    DN grandchildBaseDN = DN.decode("ou=grandchild,ou=child,o=parent");
+    String grandchildBackendID = createBackendID(grandchildBaseDN);
+    Entry grandchildBackendEntry = createBackendEntry(grandchildBackendID, true,
+                                                      grandchildBaseDN);
+
+    addOperation =
+         conn.processAdd(grandchildBackendEntry.getDN(),
+                         grandchildBackendEntry.getObjectClasses(),
+                         grandchildBackendEntry.getUserAttributes(),
+                         grandchildBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend grandchildBackend = DirectoryServer.getBackend(grandchildBackendID);
+    assertNotNull(grandchildBackend);
+    assertEquals(grandchildBackend,
+                 DirectoryServer.getBackendWithBaseDN(grandchildBaseDN));
+    assertNotNull(grandchildBackend.getParentBackend());
+    assertEquals(grandchildBackend.getParentBackend(), parentBackend);
+    assertTrue(parentBackend.getSubordinateBackends().length == 1);
+    assertFalse(grandchildBackend.entryExists(grandchildBaseDN));
+
+    // Verify that we can't create the grandchild base entry because its parent
+    // doesn't exist.
+    e = createEntry(grandchildBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.NO_SUCH_OBJECT);
+    assertFalse(grandchildBackend.entryExists(grandchildBaseDN));
+
+
+    // Add the child backend to the server and create its base entry.
+    DN childBaseDN = DN.decode("ou=child,o=parent");
+    String childBackendID = createBackendID(childBaseDN);
+    Entry childBackendEntry = createBackendEntry(childBackendID, true,
+                                                 childBaseDN);
+
+    addOperation =
+         conn.processAdd(childBackendEntry.getDN(),
+                         childBackendEntry.getObjectClasses(),
+                         childBackendEntry.getUserAttributes(),
+                         childBackendEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    Backend childBackend = DirectoryServer.getBackend(childBackendID);
+    assertNotNull(childBackend);
+    assertEquals(childBackend,
+                 DirectoryServer.getBackendWithBaseDN(childBaseDN));
+    assertNotNull(childBackend.getParentBackend());
+    assertEquals(parentBackend, childBackend.getParentBackend());
+    assertTrue(parentBackend.getSubordinateBackends().length == 1);
+    assertFalse(childBackend.entryExists(childBaseDN));
+    assertTrue(childBackend.getSubordinateBackends().length == 1);
+    assertEquals(childBackend.getSubordinateBackends()[0], grandchildBackend);
+    assertEquals(grandchildBackend.getParentBackend(), childBackend);
+
+    e = createEntry(childBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(childBackend.entryExists(childBaseDN));
+
+    // Now we can create the grandchild base entry.
+    e = createEntry(grandchildBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(grandchildBackend.entryExists(grandchildBaseDN));
+
+
+    // Verify that a subtree search can see all three entries.
+    InternalSearchOperation internalSearch =
+         conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+              SearchFilter.createFilterFromString("(objectClass=*)"));
+    assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+    assertEquals(internalSearch.getSearchEntries().size(), 3);
+
+
+    // Disable the intermediate (child) backend.  This should be allowed.
+    ArrayList<Modification> mods = new ArrayList<Modification>();
+    mods.add(new Modification(ModificationType.REPLACE,
+                              new Attribute("ds-cfg-backend-enabled",
+                                            "false")));
+    ModifyOperation modifyOperation =
+         conn.processModify(childBackendEntry.getDN(), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    // Make sure that we now only see two entries with the subtree search
+    // (and those two entries should be the parent and grandchild base entries).
+    internalSearch =
+         conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+              SearchFilter.createFilterFromString("(objectClass=*)"));
+    assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+    assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+    // Re-enable the intermediate backend.
+    mods = new ArrayList<Modification>();
+    mods.add(new Modification(ModificationType.REPLACE,
+                              new Attribute("ds-cfg-backend-enabled", "true")));
+    modifyOperation = conn.processModify(childBackendEntry.getDN(), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    // Update our reference to the child backend since the old one is no longer
+    // valid, and make sure that it got re-inserted back into the same place in
+    // the hierarchy.
+    childBackend = DirectoryServer.getBackend(childBackendID);
+    assertNotNull(childBackend);
+    assertEquals(childBackend,
+                 DirectoryServer.getBackendWithBaseDN(childBaseDN));
+    assertNotNull(childBackend.getParentBackend());
+    assertEquals(parentBackend, childBackend.getParentBackend());
+    assertTrue(parentBackend.getSubordinateBackends().length == 1);
+    assertFalse(childBackend.entryExists(childBaseDN));
+    assertTrue(childBackend.getSubordinateBackends().length == 1);
+    assertEquals(childBackend.getSubordinateBackends()[0], grandchildBackend);
+    assertEquals(grandchildBackend.getParentBackend(), childBackend);
+
+
+    // Since the memory backend that we're using for this test doesn't retain
+    // entries across stops and restarts, a subtree search below the parent
+    // should still only return two entries, which means that it's going through
+    // the entire chain of backends.
+    internalSearch =
+         conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+              SearchFilter.createFilterFromString("(objectClass=*)"));
+    assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+    assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+    // Add the child entry back into the server to get things back to the way
+    // they were before we disabled the backend.
+    e = createEntry(childBaseDN);
+    addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+                                   e.getUserAttributes(),
+                                   e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(childBackend.entryExists(childBaseDN));
+
+
+    // We should again be able to see all three entries when performing a
+    // search.
+    internalSearch =
+         conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+              SearchFilter.createFilterFromString("(objectClass=*)"));
+    assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+    assertEquals(internalSearch.getSearchEntries().size(), 3);
+
+
+    // Get rid of the entries in the proper order.
+    DeleteOperation deleteOperation =
+         conn.processDelete(grandchildBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(grandchildBackendID));
+    assertTrue(childBackend.getSubordinateBackends().length == 0);
+    assertTrue(parentBackend.getSubordinateBackends().length == 1);
+
+    deleteOperation = conn.processDelete(childBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(childBackendID));
+    assertTrue(parentBackend.getSubordinateBackends().length == 0);
+
+    deleteOperation = conn.processDelete(parentBackendEntry.getDN());
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(DirectoryServer.getBackend(parentBackendID));
+  }
+
+
+
+  /**
+   * Creates an entry that may be used to add a new backend to the server.  It
+   * will be an instance of the memory backend.
+   *
+   * @param  backendID  The backend ID to use for the backend.
+   * @param  enabled    Indicates whether the backend should be enabled.
+   * @param  baseDNs    The set of base DNs to use for the new backend.
+   *
+   * @return  An entry that may be used to add a new backend to the server.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  private Entry createBackendEntry(String backendID, boolean enabled,
+                                   DN... baseDNs)
+          throws Exception
+  {
+    assertNotNull(baseDNs);
+    assertFalse(baseDNs.length == 0);
+
+    ArrayList<String> lines = new ArrayList<String>();
+    lines.add("dn: ds-cfg-backend-id=" + backendID + ",cn=Backends,cn=config");
+    lines.add("objectClass: top");
+    lines.add("objectClass: ds-cfg-backend");
+    lines.add("ds-cfg-backend-id: " + backendID);
+    lines.add("ds-cfg-backend-class: org.opends.server.backends.MemoryBackend");
+    lines.add("ds-cfg-backend-enabled: " + String.valueOf(enabled));
+    lines.add("ds-cfg-backend-writability-mode: enabled");
+
+    for (DN dn : baseDNs)
+    {
+      lines.add("ds-cfg-backend-base-dn: " + dn.toString());
+    }
+
+    String[] lineArray = new String[lines.size()];
+    lines.toArray(lineArray);
+    return TestCaseUtils.makeEntry(lineArray);
+  }
+
+
+
+  /**
+   * Constructs a backend ID to use for a backend with the provided set of base
+   * DNs.
+   *
+   * @param  baseDNs  The set of base DNs to use when creating the backend ID.
+   *
+   * @return  The constructed backend ID based on the given base DNs.
+   */
+  private String createBackendID(DN... baseDNs)
+  {
+    StringBuilder buffer = new StringBuilder();
+
+    for (DN dn : baseDNs)
+    {
+      if (buffer.length() > 0)
+      {
+        buffer.append("___");
+      }
+
+      String ndn = dn.toNormalizedString();
+      for (int i=0; i < ndn.length(); i++)
+      {
+        char c = ndn.charAt(i);
+        if (Character.isLetterOrDigit(c))
+        {
+          buffer.append(c);
+        }
+        else
+        {
+          buffer.append('_');
+        }
+      }
+    }
+
+    return buffer.toString();
+  }
+}
+

--
Gitblit v1.10.0