From 97050c54efee9d0cc89afb5fb61b9a1c67f73f6a Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Wed, 16 Nov 2016 19:28:22 +0000
Subject: [PATCH] OPENDJ-3460 Improve management of subordinate backend and naming contexts

---
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/FakeAddOperation.java                         |    3 
 opendj-server-legacy/src/main/java/org/opends/server/api/EntryCache.java                                              |   15 
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java                            |    3 
 opendj-server-legacy/src/main/java/org/opends/server/backends/RootDSEBackend.java                                     |   31 
 opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java                                            |   29 
 opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java                           |    2 
 opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationBasis.java                                      |    6 
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java       |    7 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/FIFOEntryCache.java                                   |  108 -
 opendj-server-legacy/src/test/java/org/opends/server/backends/GenericLocalBackendTestCase.java                        |   18 
 opendj-server-legacy/src/main/java/org/opends/server/backends/LDIFBackend.java                                        |   26 
 opendj-server-legacy/src/test/java/org/opends/server/core/AddOperationTestCase.java                                   |    8 
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java                           |    4 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/ExactMatchIdentityMapper.java                         |   22 
 opendj-server-legacy/src/main/java/org/opends/server/backends/BackupBackend.java                                      |   14 
 opendj-server-legacy/src/test/java/org/opends/server/core/BackendConfigManagerTestCase.java                           |  593 ++++++++-
 opendj-server-legacy/src/test/java/org/opends/server/extensions/FIFOEntryCacheTestCase.java                           |   23 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java        |   13 
 opendj-server-legacy/src/test/java/org/opends/server/replication/GenerationIdTest.java                                |    2 
 opendj-server-legacy/src/main/java/org/opends/server/api/LocalBackend.java                                            |  116 -
 opendj-server-legacy/src/test/java/org/opends/server/replication/DependencyTest.java                                  |    2 
 opendj-server-legacy/src/test/java/org/opends/server/tasks/TasksTestCase.java                                         |    2 
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java    |   18 
 opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskBackend.java                                   |   12 
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java                   |    5 
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java  |    8 
 opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java                          |   16 
 opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LDAPBinaryOptionTestCase.java                     |    4 
 opendj-server-legacy/src/main/java/org/opends/server/tasks/ExportTask.java                                            |    2 
 opendj-server-legacy/src/test/java/org/opends/server/backends/task/TaskBackendTestCase.java                           |    4 
 opendj-server-legacy/src/test/java/org/opends/server/backends/LDIFBackendTestCase.java                                |    2 
 opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java                                                 |    6 
 opendj-server-legacy/src/test/java/org/opends/server/types/PrivilegeTestCase.java                                     |    2 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java          |   12 
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java                    |   11 
 opendj-server-legacy/src/main/java/org/opends/server/backends/TrustStoreBackend.java                                  |    8 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/FingerprintCertificateMapper.java                     |   18 
 opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java                                           |    4 
 opendj-server-legacy/src/main/java/org/opends/server/plugins/SevenBitCleanPlugin.java                                 |    5 
 opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java                                        |   82 -
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java                                |    2 
 opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java                                   |    8 
 opendj-server-legacy/src/main/java/org/opends/server/backends/MemoryBackend.java                                      |   10 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/DefaultEntryCache.java                                |   11 
 opendj-server-legacy/src/test/java/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java                  |   15 
 opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskScheduler.java                                 |    2 
 opendj-server-legacy/src/main/java/org/opends/server/tasks/BackupTask.java                                            |    4 
 opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationBasis.java                                 |    3 
 opendj-server-legacy/src/main/java/org/opends/server/plugins/UniqueAttributePlugin.java                               |   29 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/RegularExpressionIdentityMapper.java                  |   22 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/SoftReferenceEntryCache.java                          |   20 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java          |   12 
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ImportLDIFReader.java                         |    2 
 opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java                                               |    6 
 opendj-server-legacy/src/test/java/org/opends/server/core/DeleteOperationTestCase.java                                |    8 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java                  |    7 
 opendj-server-legacy/src/test/java/org/opends/server/types/TestDN.java                                                |   18 
 opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java                                    |    2 
 opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java                                   | 1903 ++++++++++++++++----------------
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java   |    2 
 opendj-server-legacy/src/test/java/org/opends/server/extensions/CommonEntryCacheTestCase.java                         |   98 -
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java    |   19 
 opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java                              |    2 
 opendj-server-legacy/src/main/java/org/opends/server/core/EntryCacheConfigManager.java                                |    4 
 opendj-server-legacy/src/test/java/org/opends/server/core/ModifyOperationTestCase.java                                |   15 
 opendj-server-legacy/src/test/java/org/opends/server/extensions/DefaultEntryCacheTestCase.java                        |   32 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java |    9 
 opendj-server-legacy/src/main/java/org/opends/server/core/PersistentSearch.java                                       |    3 
 68 files changed, 1,766 insertions(+), 1,768 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/api/EntryCache.java b/opendj-server-legacy/src/main/java/org/opends/server/api/EntryCache.java
index 1bff547..fb2ed6d 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/api/EntryCache.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/api/EntryCache.java
@@ -25,6 +25,7 @@
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.server.config.server.EntryCacheCfg;
+import org.opends.server.core.ServerContext;
 import org.opends.server.monitors.EntryCacheMonitorProvider;
 import org.forgerock.opendj.ldap.DN;
 import org.opends.server.types.Entry;
@@ -104,7 +105,8 @@
   /**
    * Initializes this entry cache implementation so that it will be
    * available for storing and retrieving entries.
-   *
+   * @param serverContext
+   *            The server context.
    * @param  configuration  The configuration to use to initialize
    *                        the entry cache.
    *
@@ -117,7 +119,7 @@
    *                                   not related to the
    *                                   configuration.
    */
-  public abstract void initializeEntryCache(T configuration)
+  public abstract void initializeEntryCache(ServerContext serverContext, T configuration)
          throws ConfigException, InitializationException;
 
   /**
@@ -299,15 +301,6 @@
   public abstract void clearBackend(String backendID);
 
   /**
-   * Removes all entries from the cache that are below the provided
-   * DN.
-   *
-   * @param  baseDN  The base DN below which all entries should be
-   *                 flushed.
-   */
-  public abstract void clearSubtree(DN baseDN);
-
-  /**
    * Attempts to react to a scenario in which it is determined that
    * the system is running low on available memory.  In this case, the
    * entry cache should attempt to free some memory if possible to try
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/api/LocalBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/api/LocalBackend.java
index c06c722..dc216ed 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/api/LocalBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/api/LocalBackend.java
@@ -18,10 +18,7 @@
 
 import static org.opends.messages.BackendMessages.*;
 
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -74,18 +71,6 @@
 public abstract class LocalBackend<C extends Configuration> extends Backend<C>
 // should have been BackendCfg instead of Configuration
 {
-  /**
-   * The backend that holds a portion of the DIT that is hierarchically above
-   * the information in this backend.
-   */
-  private LocalBackend<?> parentBackend;
-
-  /**
-   * The set of backends that hold portions of the DIT that are hierarchically
-   * below the information in this backend.
-   */
-  private LocalBackend<?>[] subordinateBackends = new LocalBackend[0];
-
   /** Indicates whether this is a private backend or one that holds user data. */
   private boolean isPrivateBackend;
 
@@ -716,89 +701,6 @@
   public abstract long getEntryCount();
 
   /**
-   * Retrieves the parent backend for this backend.
-   *
-   * @return  The parent backend for this backend, or {@code null} if
-   *          there is none.
-   */
-  public final LocalBackend<?> getParentBackend()
-  {
-    return parentBackend;
-  }
-
-  /**
-   * Specifies the parent backend for this backend.
-   *
-   * @param  parentBackend  The parent backend for this backend.
-   */
-  public final synchronized void setParentBackend(LocalBackend<?> parentBackend)
-  {
-    this.parentBackend = parentBackend;
-  }
-
-  /**
-   * Retrieves the set of subordinate backends for this backend.
-   *
-   * @return  The set of subordinate backends for this backend, or an
-   *          empty array if none exist.
-   */
-  public final LocalBackend<?>[] getSubordinateBackends()
-  {
-    return subordinateBackends;
-  }
-
-
-  /**
-   * Adds the provided backend to the set of subordinate backends for
-   * this backend.
-   *
-   * @param  subordinateBackend  The backend to add to the set of
-   *                             subordinate backends for this
-   *                             backend.
-   */
-  public final synchronized void addSubordinateBackend(LocalBackend<?> subordinateBackend)
-  {
-    LinkedHashSet<LocalBackend<?>> backendSet = new LinkedHashSet<>();
-    Collections.addAll(backendSet, subordinateBackends);
-
-    if (backendSet.add(subordinateBackend))
-    {
-      subordinateBackends = backendSet.toArray(new LocalBackend[backendSet.size()]);
-    }
-  }
-
-  /**
-   * 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 final synchronized void removeSubordinateBackend(Backend<?> subordinateBackend)
-  {
-    ArrayList<LocalBackend<?>> backendList = new ArrayList<>(subordinateBackends.length);
-
-    boolean found = false;
-    for (LocalBackend<?> b : subordinateBackends)
-    {
-      if (b.equals(subordinateBackend))
-      {
-        found = true;
-      }
-      else
-      {
-        backendList.add(b);
-      }
-    }
-
-    if (found)
-    {
-      subordinateBackends = backendList.toArray(new LocalBackend[backendList.size()]);
-    }
-  }
-
-  /**
    * Indicates whether this backend should be used to handle
    * operations for the provided entry.
    *
@@ -810,21 +712,9 @@
    */
   public final boolean handlesEntry(DN entryDN)
   {
-    for (DN dn : getBaseDNs())
-    {
-      if (entryDN.isSubordinateOrEqualTo(dn))
-      {
-        for (LocalBackend<?> b : subordinateBackends)
-        {
-          if (b.handlesEntry(entryDN))
-          {
-            return false;
-          }
-        }
-        return true;
-      }
-    }
-    return false;
+    Backend<?> backend =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().findBackendForEntry(entryDN);
+    return backend != null && backend == this;
   }
 
   /**
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/BackupBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/BackupBackend.java
index f4c4e14..fc23d7f 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/BackupBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/BackupBackend.java
@@ -373,7 +373,7 @@
     // If so, then it must point to a backup directory.  Otherwise, it must be
     // two levels below the backup base entry and must point to a specific
     // backup.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       return -1;
@@ -400,7 +400,7 @@
       }
       return count;
     }
-    else if (backupBaseDN.equals(DirectoryServer.getParentDNInSuffix(parentDN)))
+    else if (backupBaseDN.equals(serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN)))
     {
       return 0;
     }
@@ -431,7 +431,7 @@
     // If so, then it must point to a backup directory.  Otherwise, it must be
     // two levels below the backup base entry and must point to a specific
     // backup.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
@@ -441,7 +441,7 @@
     {
       return getBackupDirectoryEntry(entryDN);
     }
-    else if (backupBaseDN.equals(DirectoryServer.getParentDNInSuffix(parentDN)))
+    else if (backupBaseDN.equals(serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN)))
     {
       return getBackupEntry(entryDN);
     }
@@ -546,7 +546,7 @@
     String backupID = idValue.toString();
 
     // Next, get the backup directory from the parent DN.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null) {
       throw newConstraintViolation(ERR_BACKUP_NO_BACKUP_PARENT_DN.get(entryDN));
     }
@@ -754,7 +754,7 @@
         }
       }
     }
-    else if (backupBaseDN.equals(parentDN = DirectoryServer.getParentDNInSuffix(baseDN)))
+    else if (backupBaseDN.equals(parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(baseDN)))
     {
       Entry backupDirEntry = getBackupDirectoryEntry(baseDN);
 
@@ -774,7 +774,7 @@
     else
     {
       if (parentDN == null
-          || !backupBaseDN.equals(DirectoryServer.getParentDNInSuffix(parentDN)))
+          || !backupBaseDN.equals(serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN)))
       {
         LocalizableMessage message = ERR_BACKUP_NO_SUCH_ENTRY.get(backupBaseDN);
         throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java
index 40e767a..1538833 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java
@@ -43,7 +43,6 @@
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
-import org.forgerock.opendj.config.Configuration;
 import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.ldap.AttributeDescription;
 import org.forgerock.opendj.ldap.ByteString;
@@ -56,6 +55,7 @@
 import org.forgerock.opendj.ldap.schema.AttributeType;
 import org.forgerock.opendj.ldap.schema.CoreSchema;
 import org.forgerock.opendj.ldap.schema.ObjectClass;
+import org.forgerock.opendj.server.config.server.LocalBackendCfg;
 import org.opends.server.api.LocalBackend;
 import org.opends.server.controls.EntryChangelogNotificationControl;
 import org.opends.server.controls.ExternalChangelogRequestControl;
@@ -160,7 +160,7 @@
  *
  * @see ReplicationServer
  */
-public class ChangelogBackend extends LocalBackend<Configuration>
+public class ChangelogBackend extends LocalBackend<LocalBackendCfg>
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
@@ -242,11 +242,11 @@
   public static ChangelogBackend getInstance()
   {
     return (ChangelogBackend) DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
-        .getLocalBackend(CHANGELOG_BASE_DN);
+        .findLocalBackendForEntry(CHANGELOG_BASE_DN);
   }
 
   @Override
-  public void configureBackend(final Configuration config, ServerContext serverContext) throws ConfigException
+  public void configureBackend(final LocalBackendCfg config, ServerContext serverContext) throws ConfigException
   {
     throw new UnsupportedOperationException("The changelog backend is not configurable");
   }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/LDIFBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/LDIFBackend.java
index 1b41013..24e3fe7 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/LDIFBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/LDIFBackend.java
@@ -533,7 +533,7 @@
       }
       else
       {
-        DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+        DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
         if (parentDN != null && entryMap.containsKey(parentDN))
         {
           entryMap.put(entryDN, entry.duplicate(false));
@@ -567,7 +567,7 @@
     {
       while (true)
       {
-        parentDN = DirectoryServer.getParentDNInSuffix(parentDN);
+        parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN);
         if (parentDN == null)
         {
           return null;
@@ -592,7 +592,7 @@
       // Get the DN of the target entry's parent, if it exists.  We'll need to
       // also remove the reference to the target entry from the parent's set of
       // children.
-      DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+      DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
 
       // Make sure that the target entry exists.  If not, then fail.
       if (! entryMap.containsKey(entryDN))
@@ -606,7 +606,7 @@
             break;
           }
 
-          parentDN = DirectoryServer.getParentDNInSuffix(parentDN);
+          parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN);
         }
 
         LocalizableMessage m = ERR_LDIF_BACKEND_DELETE_NO_SUCH_ENTRY.get(entryDN);
@@ -711,7 +711,7 @@
       if (! entryMap.containsKey(entryDN))
       {
         DN matchedDN = null;
-        DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+        DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
         while (parentDN != null)
         {
           if (entryMap.containsKey(parentDN))
@@ -720,7 +720,7 @@
             break;
           }
 
-          parentDN = DirectoryServer.getParentDNInSuffix(parentDN);
+          parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN);
         }
 
         LocalizableMessage m = ERR_LDIF_BACKEND_MODIFY_NO_SUCH_ENTRY.get(entryDN);
@@ -752,7 +752,7 @@
       if (! entryMap.containsKey(currentDN))
       {
         DN matchedDN = null;
-        DN parentDN = DirectoryServer.getParentDNInSuffix(currentDN);
+        DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(currentDN);
         while (parentDN != null)
         {
           if (entryMap.containsKey(parentDN))
@@ -761,7 +761,7 @@
             break;
           }
 
-          parentDN = DirectoryServer.getParentDNInSuffix(parentDN);
+          parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN);
         }
 
         LocalizableMessage m = ERR_LDIF_BACKEND_MODDN_NO_SUCH_SOURCE_ENTRY.get(currentDN);
@@ -774,7 +774,7 @@
         throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, m);
       }
 
-      DN newParentDN = DirectoryServer.getParentDNInSuffix(newDN);
+      DN newParentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(newDN);
       if (! entryMap.containsKey(newParentDN))
       {
         throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
@@ -783,7 +783,7 @@
 
       // Remove the entry from the list of children for the old parent and
       // add the new entry DN to the set of children for the new parent.
-      DN oldParentDN = DirectoryServer.getParentDNInSuffix(currentDN);
+      DN oldParentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(currentDN);
       Set<DN> parentChildDNs = childDNs.get(oldParentDN);
       if (parentChildDNs != null)
       {
@@ -887,7 +887,7 @@
       Entry baseEntry = entryMap.get(baseDN);
       if (baseEntry == null && handlesEntry(baseDN))
       {
-        DN matchedDN = DirectoryServer.getParentDNInSuffix(baseDN);
+        DN matchedDN = serverContext.getBackendConfigManager().getParentDNInSuffix(baseDN);
         while (matchedDN != null)
         {
           if (entryMap.containsKey(matchedDN))
@@ -895,7 +895,7 @@
             break;
           }
 
-          matchedDN = DirectoryServer.getParentDNInSuffix(matchedDN);
+          matchedDN = serverContext.getBackendConfigManager().getParentDNInSuffix(matchedDN);
         }
 
         LocalizableMessage m = ERR_LDIF_BACKEND_SEARCH_NO_SUCH_BASE.get(baseDN);
@@ -1092,7 +1092,7 @@
             continue;
           }
 
-          DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+          DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
           if (parentDN == null || !entryMap.containsKey(parentDN))
           {
             LocalizableMessage m = ERR_LDIF_BACKEND_MISSING_PARENT.get(
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/MemoryBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/MemoryBackend.java
index 68b3fe0..9e7c09f 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/MemoryBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/MemoryBackend.java
@@ -313,7 +313,7 @@
     }
 
     // Get the parent DN and ensure that it exists in the backend.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
@@ -384,7 +384,7 @@
     childDNs.remove(entryDN);
     entryMap.remove(entryDN);
 
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN != null)
     {
       HashSet<DN> parentsChildren = childDNs.get(parentDN);
@@ -461,7 +461,7 @@
     }
 
     // Make sure that the parent of the new entry exists.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(e.getName());
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(e.getName());
     if (parentDN == null || !entryMap.containsKey(parentDN))
     {
       throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
@@ -498,7 +498,7 @@
     Entry baseEntry = entryMap.get(baseDN);
     if (baseEntry == null && handlesEntry(baseDN))
     {
-      DN matchedDN = DirectoryServer.getParentDNInSuffix(baseDN);
+      DN matchedDN = serverContext.getBackendConfigManager().getParentDNInSuffix(baseDN);
       while (matchedDN != null)
       {
         if (entryMap.containsKey(matchedDN))
@@ -506,7 +506,7 @@
           break;
         }
 
-        matchedDN = DirectoryServer.getParentDNInSuffix(matchedDN);
+        matchedDN = serverContext.getBackendConfigManager().getParentDNInSuffix(matchedDN);
       }
 
       LocalizableMessage message =
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/RootDSEBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/RootDSEBackend.java
index 2a70ff1..47c51e3 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/RootDSEBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/RootDSEBackend.java
@@ -21,6 +21,9 @@
 import static org.opends.messages.BackendMessages.*;
 import static org.opends.messages.ConfigMessages.*;
 import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PRIVATE;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.util.CollectionUtils.*;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
@@ -30,7 +33,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,6 +58,7 @@
 import org.opends.server.api.Backend;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.core.AddOperation;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DeleteOperation;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
@@ -324,14 +327,15 @@
     Map<AttributeType, List<Attribute>> dseUserAttrs = new HashMap<>();
     Map<AttributeType, List<Attribute>> dseOperationalAttrs = new HashMap<>();
 
-    Set<DN> publicNamingContexts = showSubordinatesNamingContexts ?
-        getAllPublicNamingContexts() : getTopLevelPublicNamingContexts();
+    BackendConfigManager manager = serverContext.getBackendConfigManager();
+    Set<DN> publicNamingContexts =
+        manager.getNamingContexts(showSubordinatesNamingContexts ?  PUBLIC : PUBLIC, TOP_LEVEL);
     Attribute publicNamingContextAttr = createAttribute(ATTR_NAMING_CONTEXTS, publicNamingContexts);
     addAttribute(publicNamingContextAttr, dseUserAttrs, dseOperationalAttrs);
 
     // Add the "ds-private-naming-contexts" attribute.
     Attribute privateNamingContextAttr = createAttribute(
-        ATTR_PRIVATE_NAMING_CONTEXTS, serverContext.getBackendConfigManager().getPrivateNamingContexts().keySet());
+        ATTR_PRIVATE_NAMING_CONTEXTS, manager.getNamingContexts(PRIVATE));
     addAttribute(privateNamingContextAttr, dseUserAttrs, dseOperationalAttrs);
 
     // Add the "supportedControl" attribute.
@@ -414,21 +418,6 @@
     return e;
   }
 
-  private Set<DN> getAllPublicNamingContexts()
-  {
-    Set<DN> namingContexts = new HashSet<>();
-    for (Backend<?> backend : serverContext.getBackendConfigManager().getAllBackends())
-    {
-      namingContexts.addAll(backend.getBaseDNs());
-    }
-    return namingContexts;
-  }
-
-  private Set<DN> getTopLevelPublicNamingContexts()
-  {
-    return new HashSet<DN>(serverContext.getBackendConfigManager().getPublicNamingContexts().keySet());
-  }
-
   private void addAll(Collection<Attribute> attributes,
       Map<AttributeType, List<Attribute>> userAttrs, Map<AttributeType, List<Attribute>> operationalAttrs)
   {
@@ -670,7 +659,7 @@
       {
         for (DN baseDN : subDNs)
         {
-          LocalBackend<?> backend = serverContext.getBackendConfigManager().getLocalBackend(baseDN);
+          LocalBackend<?> backend = serverContext.getBackendConfigManager().findLocalBackendForEntry(baseDN);
           if (backend == null)
           {
             unacceptableReasons.add(WARN_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE.get(baseDN));
@@ -711,7 +700,7 @@
         subBases = new ConcurrentHashMap<>();
         for (DN baseDN : subDNs)
         {
-          LocalBackend<?> backend = serverContext.getBackendConfigManager().getLocalBackend(baseDN);
+          LocalBackend<?> backend = serverContext.getBackendConfigManager().findLocalBackendForEntry(baseDN);
           if (backend == null)
           {
             // This is not fine.  We can't use a suffix that doesn't exist.
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/TrustStoreBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/TrustStoreBackend.java
index 579eb46..cc88fd9 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/TrustStoreBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/TrustStoreBackend.java
@@ -365,7 +365,7 @@
 
     // See if the requested entry was one level below the backend base entry.
     // If so, then it must point to a trust store entry.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN != null && parentDN.equals(getBaseDN()))
     {
       try
@@ -458,7 +458,7 @@
       throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
     }
 
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
@@ -486,7 +486,7 @@
       throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
     }
 
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null || !parentDN.equals(getBaseDN()))
     {
       LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
@@ -574,7 +574,7 @@
         }
       }
     }
-    else if (getBaseDN().equals(DirectoryServer.getParentDNInSuffix(baseDN)))
+    else if (getBaseDN().equals(serverContext.getBackendConfigManager().getParentDNInSuffix(baseDN)))
     {
       Entry certEntry = getCertEntry(baseDN);
 
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
index 9c10a92..414aeaf 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
@@ -2497,14 +2497,14 @@
    */
   private DN getMatchedDN(ReadableTransaction txn, DN targetDN) throws DirectoryException
   {
-    DN parentDN = DirectoryServer.getParentDNInSuffix(targetDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(targetDN);
     while (parentDN != null && parentDN.isSubordinateOrEqualTo(baseDN))
     {
       if (entryExists(txn, parentDN))
       {
         return parentDN;
       }
-      parentDN = DirectoryServer.getParentDNInSuffix(parentDN);
+      parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(parentDN);
     }
     return null;
   }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ImportLDIFReader.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ImportLDIFReader.java
index 0559187..cb553f4 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ImportLDIFReader.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ImportLDIFReader.java
@@ -306,7 +306,7 @@
       {
         return entryContainer;
       }
-      nodeDN = DirectoryServer.getParentDNInSuffix(nodeDN);
+      nodeDN = DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getParentDNInSuffix(nodeDN);
     }
     return null;
   }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java
index db0f99d..e2fe9af 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java
@@ -44,7 +44,6 @@
 import org.opends.server.backends.pluggable.spi.StorageStatus;
 import org.opends.server.backends.pluggable.spi.WriteOperation;
 import org.opends.server.backends.pluggable.spi.WriteableTransaction;
-import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.SearchOperation;
 import org.opends.server.core.ServerContext;
 import org.forgerock.opendj.ldap.DN;
@@ -377,7 +376,7 @@
       ec = entryContainers.get(nodeDN);
       if (ec == null)
       {
-        nodeDN = DirectoryServer.getParentDNInSuffix(nodeDN);
+        nodeDN = serverContext.getBackendConfigManager().getParentDNInSuffix(nodeDN);
       }
     }
 
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java
index 51da04f..2b101b1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java
@@ -968,7 +968,7 @@
     {
       return null;
     }
-    return DirectoryServer.getParentDNInSuffix(dn);
+    return DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getParentDNInSuffix(dn);
   }
 
   /** This class maintain the number of children for a given dn. */
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskBackend.java
index f000dbf..4ce5668 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskBackend.java
@@ -352,7 +352,7 @@
       return taskScheduler.getRecurringTaskCount();
     }
 
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       return -1;
@@ -399,7 +399,7 @@
         return taskScheduler.getRecurringTaskParentEntry();
       }
 
-      DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+      DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
       if (parentDN == null)
       {
         return null;
@@ -434,7 +434,7 @@
 
     // Get the DN for the entry and then get its parent.
     DN entryDN = e.getName();
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
 
     if (parentDN == null)
     {
@@ -473,7 +473,7 @@
   {
     // Get the parent for the provided entry DN.  It must be either the
     // scheduled or recurring task parent DN.
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       LocalizableMessage message = ERR_TASKBE_DELETE_INVALID_ENTRY.get(entryDN);
@@ -558,7 +558,7 @@
     {
       // Get the parent for the provided entry DN.  It must be either the
       // scheduled or recurring task parent DN.
-      DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+      DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
       if (parentDN == null)
       {
         LocalizableMessage message = ERR_TASKBE_MODIFY_INVALID_ENTRY.get(entryDN);
@@ -803,7 +803,7 @@
     }
     else
     {
-      DN parentDN = DirectoryServer.getParentDNInSuffix(baseDN);
+      DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(baseDN);
       if (parentDN == null)
       {
         LocalizableMessage message = ERR_TASKBE_SEARCH_INVALID_BASE.get(baseDN);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskScheduler.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskScheduler.java
index 556375d..0a6b72b 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskScheduler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskScheduler.java
@@ -1074,7 +1074,7 @@
         }
         else
         {
-          DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+          DN parentDN = serverContext.getBackendConfigManager().getParentDNInSuffix(entryDN);
           if (parentDN == null)
           {
             logger.error(ERR_TASKSCHED_ENTRY_HAS_NO_PARENT, entryDN, taskBackend.getTaskRootDN());
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationBasis.java
index 1bff6ad..8f033fa 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationBasis.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationBasis.java
@@ -580,11 +580,13 @@
   public void updateOperationErrMsgAndResCode()
   {
     DN entryDN = getEntryDN();
-    DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+    BackendConfigManager backendConfigManager =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+    DN parentDN = backendConfigManager.getParentDNInSuffix(entryDN);
     if (parentDN == null)
     {
       // Either this entry is a suffix or doesn't belong in the directory.
-      if (DirectoryServer.isNamingContext(entryDN))
+      if (backendConfigManager.containsLocalNamingContext(entryDN))
       {
         // This is fine.  This entry is one of the configured suffixes.
         return;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java b/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
index ff8167b..e134eaf 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
@@ -17,13 +17,16 @@
 package org.opends.server.core;
 
 import static org.forgerock.opendj.ldap.ResultCode.*;
-import static org.forgerock.util.Reject.ifNull;
 import static org.opends.messages.ConfigMessages.*;
 import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.core.DirectoryServer.*;
 import static org.opends.server.util.StaticUtils.*;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -31,12 +34,9 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2;
@@ -53,6 +53,7 @@
 import org.forgerock.opendj.server.config.server.LocalBackendCfg;
 import org.forgerock.opendj.server.config.server.RootCfg;
 import org.forgerock.opendj.server.config.server.RootDSEBackendCfg;
+import org.forgerock.util.Reject;
 import org.opends.server.api.LocalBackend;
 import org.opends.server.api.Backend;
 import org.opends.server.api.LocalBackendInitializationListener;
@@ -65,11 +66,14 @@
 import org.opends.server.types.InitializationException;
 import org.opends.server.types.WritabilityMode;
 
+import com.forgerock.opendj.util.Iterables;
+import com.forgerock.opendj.util.Predicate;
+
 /**
- * Responsible for managing the configuration of backends defined in the Directory Server.
+ * Responsible for managing the lifecycle of backends in the Directory Server.
  * <p>
- * It will perform the necessary initialization of those backends when the server is first
- * started, and then will manage any changes to them while the server is running.
+ * It performs the necessary initialization of the backends when the server is first
+ * started, and then manages any changes to them while the server is running.
  */
 public class BackendConfigManager implements
      ConfigurationChangeListener<BackendCfg>,
@@ -78,20 +82,26 @@
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
-  /** The mapping beetwen backends IDs and local backend implementations. */
-  private final Map<String, LocalBackend<?>> localBackends = new ConcurrentHashMap<>();
+  /** The mapping between backends IDs and local backend implementations. */
+  private final Map<String, LocalBackend<?>> localBackendsById = new ConcurrentHashMap<>();
 
-  /** The mapping between configuration entry DNs and their corresponding backend implementations. */
-  private final Map<DN, Backend<? extends BackendCfg>> registeredBackends = new ConcurrentHashMap<>();
+  /** The mapping between backend configuration names and backend implementations. */
+  private final Map<DN, Backend<? extends BackendCfg>> configuredBackends = new ConcurrentHashMap<>();
 
   /** The set of local backend initialization listeners. */
   private final Set<LocalBackendInitializationListener> initializationListeners = new CopyOnWriteArraySet<>();
 
-  private final ServerContext serverContext;
-  private final BaseDnRegistry localBackendsRegistry;
+  /** Contains all relationships between the base DNs and the backends (at the exclusion of RootDSE backend). */
+  private volatile Registry registry = new Registry();
 
+  /** The Root DSE backend, which is managed separately from other backends. */
   private RootDSEBackend rootDSEBackend;
 
+  /** Lock for updates: add, change and delete operations on backends. */
+  private final ReentrantLock writeLock = new ReentrantLock();
+
+  private final ServerContext serverContext;
+
   /**
    * Creates a new instance of this backend config manager.
    *
@@ -101,7 +111,6 @@
   public BackendConfigManager(ServerContext serverContext)
   {
     this.serverContext = serverContext;
-    this.localBackendsRegistry = new BaseDnRegistry();
   }
 
   /**
@@ -120,7 +129,9 @@
   public void initializeBackendConfig(Collection<String> backendIDsToStart)
          throws ConfigException, InitializationException
   {
-    initializeConfigurationBackend();
+    final ConfigurationBackend configBackend =
+        new ConfigurationBackend(serverContext, DirectoryServer.getConfigurationHandler());
+    initializeBackend(configBackend, configBackend.getBackendCfg());
 
     // Register add and delete listeners.
     RootCfg root = serverContext.getRootConfig();
@@ -187,62 +198,63 @@
    */
   public void initializeBackends(Collection<String> backendIDsToStart, RootCfg root) throws ConfigException
   {
-    // Initialize existing backends.
-    for (String name : root.listBackends())
+    writeLock.lock();
+    try
     {
-      // Get the handler's configuration.
-      // This will decode and validate its properties.
-      final BackendCfg backendCfg = root.getBackend(name);
-      final String backendID = backendCfg.getBackendId();
-      if (!backendIDsToStart.isEmpty() && !backendIDsToStart.contains(backendID))
+      // Initialize existing backends.
+      for (String name : root.listBackends())
       {
-        continue;
-      }
-      if (hasLocalBackend(backendID))
-      {
-        // Skip this backend if it is already initialized and registered as available.
-        continue;
-      }
+        // Get the handler's configuration.
+        // This will decode and validate its properties.
+        final BackendCfg backendCfg = root.getBackend(name);
+        final String backendID = backendCfg.getBackendId();
+        if (!backendIDsToStart.isEmpty() && !backendIDsToStart.contains(backendID))
+        {
+          continue;
+        }
+        if (hasLocalBackend(backendID))
+        {
+          // Skip this backend if it is already initialized and registered as available.
+          continue;
+        }
 
-      // Register as a change listener for this backend so that we can be
-      // notified when it is disabled or enabled.
-      backendCfg.addChangeListener(this);
+        // Register as a change listener for this backend so that we can be
+        // notified when it is disabled or enabled.
+        backendCfg.addChangeListener(this);
 
-      final DN backendDN = backendCfg.dn();
-      if (!backendCfg.isEnabled())
-      {
-        logger.debug(INFO_CONFIG_BACKEND_DISABLED, backendDN);
-        continue;
+        final DN backendDN = backendCfg.dn();
+        if (!backendCfg.isEnabled())
+        {
+          logger.debug(INFO_CONFIG_BACKEND_DISABLED, backendDN);
+          continue;
+        }
+
+        // See if the entry contains an attribute that specifies the class name
+        // for the backend implementation.  If it does, then load it and make
+        // sure that it's a valid backend implementation.  There is no such
+        // attribute, the specified class cannot be loaded, or it does not
+        // contain a valid backend implementation, then log an error and skip it.
+        String className = backendCfg.getJavaClass();
+
+        Backend<? extends BackendCfg> backend;
+        try
+        {
+          backend = loadBackendClass(className).newInstance();
+        }
+        catch (Exception e)
+        {
+          logger.traceException(e);
+          logger.error(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE, className, backendDN, stackTraceToSingleLineString(e));
+          continue;
+        }
+
+        initializeBackend(backend, backendCfg);
       }
-
-      // See if the entry contains an attribute that specifies the class name
-      // for the backend implementation.  If it does, then load it and make
-      // sure that it's a valid backend implementation.  There is no such
-      // attribute, the specified class cannot be loaded, or it does not
-      // contain a valid backend implementation, then log an error and skip it.
-      String className = backendCfg.getJavaClass();
-
-      Backend<? extends BackendCfg> backend;
-      try
-      {
-        backend = loadBackendClass(className).newInstance();
-      }
-      catch (Exception e)
-      {
-        logger.traceException(e);
-        logger.error(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE, className, backendDN, stackTraceToSingleLineString(e));
-        continue;
-      }
-
-      initializeBackend(backend, backendCfg);
     }
-  }
-
-  private void initializeConfigurationBackend() throws InitializationException
-  {
-    final ConfigurationBackend configBackend =
-        new ConfigurationBackend(serverContext, DirectoryServer.getConfigurationHandler());
-    initializeBackend(configBackend, configBackend.getBackendCfg());
+    finally
+    {
+      writeLock.unlock();
+    }
   }
 
   private void initializeBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg)
@@ -279,24 +291,6 @@
   }
 
   /**
-   * Returns the provided backend instance as a LocalBackend.
-   *
-   * @param backend
-   *            A backend
-   * @return a local backend
-   * @throws IllegalArgumentException
-   *            If the provided backend is not a LocalBackend
-   */
-  public static LocalBackend<?> asLocalBackend(Backend<?> backend)
-  {
-    if (backend instanceof LocalBackend)
-    {
-      return (LocalBackend<?>) backend;
-    }
-    throw new IllegalArgumentException("Backend " + backend.getBackendID() + " is not a local backend");
-  }
-
-  /**
    * Acquire a shared lock on this backend. This will prevent operations like LDIF import or restore
    * from occurring while the backend is active.
    */
@@ -358,7 +352,7 @@
    */
   public Set<Backend<?>> getAllBackends()
   {
-    return new HashSet<Backend<?>>(registeredBackends.values());
+    return new HashSet<Backend<?>>(registry.backendsByName.values());
   }
 
   /**
@@ -368,7 +362,7 @@
    */
   public Set<LocalBackend<?>> getLocalBackends()
   {
-    return new HashSet<LocalBackend<?>>(localBackends.values());
+    return new HashSet<LocalBackend<?>>(registry.localBackendsByName.values());
   }
 
   /**
@@ -382,7 +376,11 @@
    */
   public LocalBackend<?> getLocalBackendWithBaseDN(DN baseDN)
   {
-    return localBackendsRegistry.getBackendWithBaseDN(baseDN);
+    if (baseDN.isRootDN())
+    {
+      return rootDSEBackend;
+    }
+    return registry.localBackendsByName.get(baseDN);
   }
 
   /**
@@ -396,33 +394,72 @@
   }
 
   /**
+   * Retrieves the DN that is the immediate parent for this DN. This method does take the server's
+   * naming context configuration into account, so if the current DN is a naming context for the
+   * server, then it will not be considered to have a parent.
+   *
+   * @param dn
+   *          the
+   * @return The DN that is the immediate parent for this DN, or {@code null} if this DN does not
+   *         have a parent (either because there is only a single RDN component or because this DN
+   *         is a suffix defined in the server).
+   */
+  public DN getParentDNInSuffix(DN dn)
+  {
+    if (dn.size() <= 1 || containsLocalNamingContext(dn))
+    {
+      return null;
+    }
+    return dn.parent();
+  }
+
+  /**
+   * Retrieves the backend that should be used to handle operations on the specified entry.
+   *
+   * @param entryDN
+   *          The DN of the entry for which to retrieve the corresponding backend.
+   * @return The backend or {@code null} if no appropriate backend
+   *         is registered with the server.
+   */
+  public Backend<?> findBackendForEntry(final DN entryDN)
+  {
+    if (entryDN.isRootDN())
+    {
+      return rootDSEBackend;
+    }
+    Registry reg = registry;
+    DN baseDN = reg.findNamingContextForEntry(entryDN);
+    return baseDN != null ? reg.backendsByName.get(baseDN) : null;
+  }
+
+  /**
+   * Retrieves the naming context that should be used to handle operations
+   * on the specified entry.
+   *
+   * @param entryDN
+   *          The DN of the entry for which to retrieve the corresponding naming context.
+   * @return The naming context or {@code null} if no appropriate naming context
+   *         is registered with the server.
+   */
+  public DN findNamingContextForEntry(final DN entryDN)
+  {
+    Registry reg = registry;
+    return reg.findNamingContextForEntry(entryDN);
+  }
+
+  /**
    * Retrieves the local backend and the corresponding baseDN that should be used to handle operations
    * on the specified entry.
    *
    * @param entryDN
    *          The DN of the entry for which to retrieve the corresponding backend.
-   * @return The local backend with its matching base DN or {@code null} if no appropriate backend
+   * @return The local backend with its matching base DN or {@code null} if no appropriate local backend
    *         is registered with the server.
    */
-  public BackendAndName getLocalBackendAndName(DN entryDN)
+  public LocalBackend<?> findLocalBackendForEntry(DN entryDN)
   {
-    return entryDN.isRootDN() ?
-        new BackendAndName(getRootDSEBackend(), entryDN) : localBackendsRegistry.getBackendAndName(entryDN);
-  }
-
-  /**
-   * Retrieves the local backend that should be used to handle operations
-   * on the specified entry.
-   *
-   * @param entryDN
-   *          The DN of the entry for which to retrieve the corresponding backend.
-   * @return The local backend or {@code null} if no appropriate backend
-   *         is registered with the server.
-   */
-  public LocalBackend<?> getLocalBackend(DN entryDN)
-  {
-    BackendAndName backend = getLocalBackendAndName(entryDN);
-    return backend != null ? backend.getBackend() : null;
+    Backend<?> backend = findBackendForEntry(entryDN);
+    return backend != null && backend instanceof LocalBackend ? (LocalBackend<?>)backend : null;
   }
 
   /**
@@ -433,9 +470,9 @@
    * @return the local backend, or {@code null} if there is no local backend registered with the
    *            specified id.
    */
-  public LocalBackend<?> getLocalBackend(String backendId)
+  public LocalBackend<?> getLocalBackendById(String backendId)
   {
-    return localBackends.get(backendId);
+    return localBackendsById.get(backendId);
   }
 
   /**
@@ -447,92 +484,78 @@
    */
   public boolean hasLocalBackend(String backendID)
   {
-    return localBackends.containsKey(backendID);
+    return localBackendsById.containsKey(backendID);
   }
 
   /**
-   * Retrieves the set of subordinate backends of the backend that corresponds to provided base DN.
+   * Retrieves the set of subordinate backends of the provided backend.
    *
-   * @param baseDN
-   *          The base DN for which to retrieve the subordinates backends.
-   * @return The set of subordinates backends (and associated base DN), which is never {@code null}
+   * @param backend
+   *          The backend for which to retrieve the subordinates backends.
+   * @return The set of subordinates backends, which is never {@code null}
    */
-  public Set<BackendAndName> getSubordinateBackends(DN baseDN)
+  public Set<Backend<?>> getSubordinateBackends(Backend<?> backend)
   {
-    final Set<BackendAndName> subs = new HashSet<>();
-    if (baseDN.isRootDN())
+    Registry reg = registry;
+    final Set<Backend<?>> subs = new HashSet<>();
+    for (DN baseDN : backend.getBaseDNs())
     {
-      Map<DN, LocalBackend<?>> namingContexts = getAllPublicNamingContexts();
-      for (Map.Entry<DN, LocalBackend<?>> b : namingContexts.entrySet())
+      for (Backend<?> b : reg.getSubordinateBackends(baseDN))
       {
-        subs.add(new BackendAndName(b.getValue(), b.getKey()));
-      }
-      return subs;
-    }
-
-    LocalBackend<?> backend = getLocalBackendWithBaseDN(baseDN);
-    if (backend == null)
-    {
-      return subs;
-    }
-    for (LocalBackend<?> subordinate : backend.getSubordinateBackends())
-    {
-      for (DN subordinateDN : subordinate.getBaseDNs())
-      {
-        if (subordinateDN.isSubordinateOrEqualTo(baseDN))
-        {
-          subs.add(new BackendAndName(subordinate, subordinateDN));
-        }
+        subs.add(b);
       }
     }
     return subs;
   }
 
   /**
-   * Gets the mapping of registered public naming contexts, not including
-   * sub-suffixes, to their associated backend.
+   * Retrieves the set of local naming contexts that are subordinates of the naming context
+   * that should be used to handle operations on the specified entry.
    *
-   * @return mapping from naming context to backend
+   * @param entryDN
+   *          The DN of the entry for which to retrieve the corresponding naming context.
+   * @return The naming context or {@code null} if no appropriate naming context
+   *         is registered with the server.
    */
-  public Map<DN, LocalBackend<?>> getPublicNamingContexts()
+  public Set<DN> findSubordinateLocalNamingContextsForEntry(DN entryDN)
   {
-    return localBackendsRegistry.getPublicNamingContextsMap();
+    Registry reg = registry;
+    DN baseDN = findNamingContextForEntry(entryDN);
+    if (baseDN == null)
+    {
+      return null;
+    }
+    return reg.getSubordinateLocalNamingContexts(baseDN);
   }
 
   /**
-   * Gets the mapping of registered public naming contexts, including sub-suffixes,
-   * to their associated backend.
+   * Retrieves naming contexts corresponding to backends, filtered with the combination of
+   * all provided filters.
    *
-   * @return mapping from naming context to backend
+   * @param filters
+   *            filter the naming contexts
+   * @see NamingContext
+   * @return the DNs of naming contexts for which all the filters apply.
    */
-  public Map<DN, LocalBackend<?>> getAllPublicNamingContexts()
+  public Set<DN> getNamingContexts(final NamingContextFilter... filters)
   {
-    return localBackendsRegistry.getAllPublicNamingContextsMap();
+    Registry reg = registry;
+    return reg.getNamingContexts(filters);
   }
 
   /**
-   * Gets the mapping of registered private naming contexts to their
-   * associated backend.
-   *
-   * @return mapping from naming context to backend
-   */
-  public Map<DN, LocalBackend<?>> getPrivateNamingContexts()
-  {
-    return localBackendsRegistry.getPrivateNamingContextsMap();
-  }
-
-  /**
-   * Indicates whether the specified DN is contained in the backends as
-   * a naming contexts.
+   * Indicates whether the specified DN is contained in the local backends as
+   * a naming context.
    *
    * @param  dn  The DN for which to make the determination.
    *
    * @return  {@code true} if the specified DN is a naming context in one
    *          backend, or {@code false} if it is not.
    */
-  public boolean containsNamingContext(DN dn)
+  public boolean containsLocalNamingContext(DN dn)
   {
-    return localBackendsRegistry.containsNamingContext(dn);
+    Registry reg = registry;
+    return reg.containsLocalNamingContext(dn);
   }
 
   /**
@@ -548,12 +571,19 @@
    * @throws DirectoryException
    *           If a problem occurs while attempting to register the provided base DN.
    */
-  public void registerBaseDN(DN baseDN, LocalBackend<?> backend, boolean isPrivate) throws DirectoryException
+  public void registerBaseDN(DN baseDN, Backend<? extends BackendCfg> backend, boolean isPrivate)
+      throws DirectoryException
   {
-    List<LocalizableMessage> warnings = localBackendsRegistry.registerBaseDN(baseDN, backend, isPrivate);
-    for (LocalizableMessage warning : warnings)
+    writeLock.lock();
+    try
     {
-      logger.error(warning);
+      Registry newRegister = registry.copy();
+      newRegister.registerBaseDN(baseDN, backend, isPrivate);
+      registry = newRegister;
+    }
+    finally
+    {
+      writeLock.unlock();
     }
   }
 
@@ -569,20 +599,29 @@
    */
   public void registerLocalBackend(LocalBackend<?> backend) throws DirectoryException
   {
-    ifNull(backend);
+    Reject.ifNull(backend);
     String backendID = backend.getBackendID();
-    ifNull(backendID);
-    if (localBackends.containsKey(backendID))
-    {
-      LocalizableMessage message = ERR_REGISTER_BACKEND_ALREADY_EXISTS.get(backendID);
-      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
-    }
-    localBackends.put(backendID, backend);
+    Reject.ifNull(backendID);
 
-    LocalBackendMonitor monitor = new LocalBackendMonitor(backend);
-    monitor.initializeMonitorProvider(null);
-    backend.setBackendMonitor(monitor);
-    registerMonitorProvider(monitor);
+    writeLock.lock();
+    try
+    {
+      if (localBackendsById.containsKey(backendID))
+      {
+        LocalizableMessage message = ERR_REGISTER_BACKEND_ALREADY_EXISTS.get(backendID);
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+      }
+      localBackendsById.put(backendID, backend);
+
+      LocalBackendMonitor monitor = new LocalBackendMonitor(backend);
+      monitor.initializeMonitorProvider(null);
+      backend.setBackendMonitor(monitor);
+      registerMonitorProvider(monitor);
+    }
+    finally
+    {
+      writeLock.unlock();
+    }
   }
 
   /**
@@ -605,10 +644,16 @@
    */
   public void deregisterBaseDN(DN baseDN) throws DirectoryException
   {
-    List<LocalizableMessage> warnings = localBackendsRegistry.deregisterBaseDN(baseDN);
-    for (LocalizableMessage error : warnings)
+    writeLock.lock();
+    try
     {
-      logger.error(error);
+      Registry newRegister = registry.copy();
+      newRegister.deregisterBaseDN(baseDN);
+      registry = newRegister;
+    }
+    finally
+    {
+      writeLock.unlock();
     }
   }
 
@@ -630,33 +675,36 @@
    */
   public void deregisterLocalBackend(LocalBackend<?> backend)
   {
-    ifNull(backend);
-    localBackends.remove(backend.getBackendID());
-    LocalBackendMonitor monitor = backend.getBackendMonitor();
-    if (monitor != null)
+    Reject.ifNull(backend);
+    writeLock.lock();
+    try
     {
-      deregisterMonitorProvider(monitor);
-      monitor.finalizeMonitorProvider();
-      backend.setBackendMonitor(null);
+      localBackendsById.remove(backend.getBackendID());
+      LocalBackendMonitor monitor = backend.getBackendMonitor();
+      if (monitor != null)
+      {
+        deregisterMonitorProvider(monitor);
+        monitor.finalizeMonitorProvider();
+        backend.setBackendMonitor(null);
+      }
+    }
+    finally
+    {
+      writeLock.unlock();
     }
   }
 
   @Override
-  public boolean isConfigurationChangeAcceptable(
-       BackendCfg configEntry,
-       List<LocalizableMessage> unacceptableReason)
+  public boolean isConfigurationChangeAcceptable(BackendCfg configEntry, List<LocalizableMessage> unacceptableReason)
   {
     DN backendDN = configEntry.dn();
-
     Set<DN> baseDNs = configEntry.getBaseDN();
 
-    // 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(backendDN);
+    Backend<? extends BackendCfg> backend = configuredBackends.get(backendDN);
     if (backend != null)
     {
-      LinkedHashSet<DN> removedDNs = new LinkedHashSet<>(backend.getBaseDNs());
-      LinkedHashSet<DN> addedDNs = new LinkedHashSet<>(baseDNs);
+      Set<DN> removedDNs = new LinkedHashSet<>(backend.getBaseDNs());
+      Set<DN> addedDNs = new LinkedHashSet<>(baseDNs);
       Iterator<DN> iterator = removedDNs.iterator();
       while (iterator.hasNext())
       {
@@ -667,48 +715,39 @@
         }
       }
 
-      if (backend instanceof LocalBackend<?>)
+      // Copy the registry and perform the requested changes to see if it complains.
+      Registry registryCopy = registry.copyForCheckingChanges();
+      for (DN dn : removedDNs)
       {
-        // Copy the registry and make the requested changes to see if it complains.
-        LocalBackend<?> localBackend = (LocalBackend<?>) backend;
-        BaseDnRegistry registry = localBackendsRegistry.copy();
-        for (DN dn : removedDNs)
+        try
         {
-          try
-          {
-            registry.deregisterBaseDN(dn);
-          }
-          catch (DirectoryException de)
-          {
-            logger.traceException(de);
-
-            unacceptableReason.add(de.getMessageObject());
-            return false;
-          }
+          registryCopy.deregisterBaseDN(dn);
         }
-
-        for (DN dn : addedDNs)
+        catch (DirectoryException de)
         {
-          try
-          {
-            registry.registerBaseDN(dn, localBackend, false);
-          }
-          catch (DirectoryException de)
-          {
-            logger.traceException(de);
+          logger.traceException(de);
+          unacceptableReason.add(de.getMessageObject());
+          return false;
+        }
+      }
 
-            unacceptableReason.add(de.getMessageObject());
-            return false;
-          }
+      for (DN dn : addedDNs)
+      {
+        try
+        {
+          registryCopy.registerBaseDN(dn, backend, false);
+        }
+        catch (DirectoryException de)
+        {
+          logger.traceException(de);
+          unacceptableReason.add(de.getMessageObject());
+          return false;
         }
       }
     }
     else if (configEntry.isEnabled())
     {
-      /*
-       * If the backend was not enabled, it has not been registered with directory server, so
-       * no listeners will be registered at the lower layers. Verify as it was an add.
-       */
+      // Backend is added
       String className = configEntry.getJavaClass();
       try
       {
@@ -733,10 +772,6 @@
         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
-    // backend, then the backend itself will need to make that determination.
     return true;
   }
 
@@ -744,116 +779,98 @@
   public ConfigChangeResult applyConfigurationChange(BackendCfg cfg)
   {
     DN backendDN = cfg.dn();
-    Backend<? extends BackendCfg> backend = registeredBackends.get(backendDN);
     final ConfigChangeResult ccr = new ConfigChangeResult();
-
-    // See if the entry contains an attribute that indicates whether the
-    // backend should be enabled.
-    boolean needToEnable = false;
+    writeLock.lock();
     try
     {
-      if (cfg.isEnabled())
-      {
-        // The backend is marked as enabled.  See if that is already true.
-        if (backend == null)
-        {
-          needToEnable = true;
-        } // else already enabled, no need to do anything.
-      }
-      else
-      {
-        // The backend is marked as disabled.  See if that is already true.
-        if (backend != null)
-        {
-          // It isn't disabled, so we will do so now and deregister it from the
-          // Directory Server.
-          deregisterBackend(backendDN, backend);
+      Backend<? extends BackendCfg> backend = configuredBackends.get(backendDN);
 
-          backend.finalizeBackend();
-
-          releaseSharedLock(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend, backend.getBackendID());
-
-          return ccr;
-        } // else already disabled, no need to do anything.
-      }
-    }
-    catch (Exception e)
-    {
-      logger.traceException(e);
-
-      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
-      ccr.addMessage(ERR_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE.get(backendDN,
-          stackTraceToSingleLineString(e)));
-      return ccr;
-    }
-
-    // See if the entry contains an attribute that specifies the class name
-    // for the backend implementation.  If it does, then load it and make sure
-    // that it's a valid backend implementation.  There is no such attribute,
-    // the specified class cannot be loaded, or it does not contain a valid
-    // backend implementation, then log an error and skip it.
-    String className = cfg.getJavaClass();
-
-    // See if this backend is currently active and if so if the name of the class is the same.
-    if (backend != null && !className.equals(backend.getClass().getName()))
-    {
-      // It is not the same. Try to load it and see if it is a valid backend implementation.
+      boolean needToEnable = false;
       try
       {
-        Class<?> backendClass = DirectoryServer.loadClass(className);
-        if (LocalBackend.class.isAssignableFrom(backendClass))
+        if (cfg.isEnabled())
         {
-          // It appears to be a valid backend class.  We'll return that the
-          // change is successful, but indicate that some administrative
-          // action is required.
-          ccr.addMessage(NOTE_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS.get(
-              backendDN, backend.getClass().getName(), className));
-          ccr.setAdminActionRequired(true);
+          if (backend == null)
+          {
+            needToEnable = true;
+          }
         }
         else
         {
-          // It is not a valid backend class.  This is an error.
-          ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-          ccr.addMessage(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN));
+          if (backend != null)
+          {
+            deregisterBackend(backendDN, backend);
+            backend.finalizeBackend();
+            releaseSharedLock(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend, backend.getBackendID());
+            return ccr;
+          }
         }
-        return ccr;
       }
       catch (Exception e)
       {
         logger.traceException(e);
 
         ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
-        ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(
-                className, backendDN, stackTraceToSingleLineString(e)));
-        return ccr;
-      }
-    }
-
-
-    // If we've gotten here, then that should mean that we need to enable the
-    // backend.  Try to do so.
-    if (needToEnable)
-    {
-      try
-      {
-        backend = loadBackendClass(className).newInstance();
-      }
-      catch (Exception e)
-      {
-        // It is not a valid backend class.  This is an error.
-        ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-        ccr.addMessage(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN));
+        ccr.addMessage(ERR_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE.get(backendDN,
+            stackTraceToSingleLineString(e)));
         return ccr;
       }
 
-      initializeBackend(backend, cfg, ccr);
-      return ccr;
-    }
-    else if (ccr.getResultCode() == ResultCode.SUCCESS && backend != null)
-    {
-      setLocalBackendWritabilityMode(backend, cfg);
-    }
+      String className = cfg.getJavaClass();
+      if (backend != null && !className.equals(backend.getClass().getName()))
+      {
+        // Need to check if it is a valid backend implementation.
+        try
+        {
+          Class<?> backendClass = DirectoryServer.loadClass(className);
+          if (LocalBackend.class.isAssignableFrom(backendClass))
+          {
+            ccr.addMessage(NOTE_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS.get(
+                backendDN, backend.getClass().getName(), className));
+            ccr.setAdminActionRequired(true);
+          }
+          else
+          {
+            ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+            ccr.addMessage(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN));
+          }
+          return ccr;
+        }
+        catch (Exception e)
+        {
+          logger.traceException(e);
 
+          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
+          ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(
+                  className, backendDN, stackTraceToSingleLineString(e)));
+          return ccr;
+        }
+      }
+
+      if (needToEnable)
+      {
+        try
+        {
+          backend = loadBackendClass(className).newInstance();
+        }
+        catch (Exception e)
+        {
+          ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+          ccr.addMessage(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN));
+          return ccr;
+        }
+        initializeBackend(backend, cfg, ccr);
+        return ccr;
+      }
+      else if (ccr.getResultCode() == ResultCode.SUCCESS && backend != null)
+      {
+        setLocalBackendWritabilityMode(backend, cfg);
+      }
+    }
+    finally
+    {
+      writeLock.unlock();
+    }
     return ccr;
   }
 
@@ -874,7 +891,6 @@
       catch (Exception e)
       {
         logger.traceException(e);
-
         LocalizableMessage message =
             WARN_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND.get(backendCfg.getBackendId(), getExceptionMessage(e));
         logger.error(message);
@@ -890,7 +906,7 @@
         listener.performBackendPostInitializationProcessing(localBackend);
       }
 
-      registeredBackends.put(backendCfg.dn(), backend);
+      configuredBackends.put(backendCfg.dn(), backend);
       return true;
     }
     throw new RuntimeException("registerBackend() is not yet supported for proxy backend.");
@@ -902,10 +918,6 @@
        List<LocalizableMessage> unacceptableReason)
   {
     DN backendDN = configEntry.dn();
-
-
-    // See if the entry contains an attribute that specifies the backend ID.  If
-    // it does not, then skip it.
     String backendID = configEntry.getBackendId();
     if (hasLocalBackend(backendID))
     {
@@ -913,21 +925,7 @@
       return false;
     }
 
-
-    // See if the entry contains an attribute that specifies the set of base DNs
-    // for the backend.  If it does not, then skip it.
-    Set<DN> baseList = configEntry.getBaseDN();
-    DN[] baseDNs = new DN[baseList.size()];
-    baseList.toArray(baseDNs);
-
-
-    // See if the entry contains an attribute that specifies the class name
-    // for the backend implementation.  If it does, then load it and make sure
-    // that it's a valid backend implementation.  There is no such attribute,
-    // the specified class cannot be loaded, or it does not contain a valid
-    // backend implementation, then log an error and skip it.
     String className = configEntry.getJavaClass();
-
     Backend<BackendCfg> backend;
     try
     {
@@ -936,38 +934,28 @@
     catch (Exception e)
     {
       logger.traceException(e);
-
-      unacceptableReason.add(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(
-              className, backendDN, stackTraceToSingleLineString(e)));
+      unacceptableReason.add(
+          ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(className, backendDN, stackTraceToSingleLineString(e)));
       return false;
     }
 
-
-    // Make sure that all of the base DNs are acceptable for use in the server.
-    if (backend instanceof LocalBackend<?>)
+    // Copy the registry and perform the requested changes to see if it complains.
+    Registry registryCopy = registry.copyForCheckingChanges();
+    for (DN baseDN : configEntry.getBaseDN())
     {
-      BaseDnRegistry registry = localBackendsRegistry.copy();
-      for (DN baseDN : baseDNs)
+      if (baseDN.isRootDN())
       {
-        if (baseDN.isRootDN())
-        {
-          unacceptableReason.add(ERR_CONFIG_BACKEND_BASE_IS_EMPTY.get(backendDN));
-          return false;
-        }
-        try
-        {
-          registry.registerBaseDN(baseDN, (LocalBackend<?>) backend, false);
-        }
-        catch (DirectoryException de)
-        {
-          unacceptableReason.add(de.getMessageObject());
-          return false;
-        }
-        catch (Exception e)
-        {
-          unacceptableReason.add(getExceptionMessage(e));
-          return false;
-        }
+        unacceptableReason.add(ERR_CONFIG_BACKEND_BASE_IS_EMPTY.get(backendDN));
+        return false;
+      }
+      try
+      {
+        registryCopy.registerBaseDN(baseDN, backend, false);
+      }
+      catch (DirectoryException de)
+      {
+        unacceptableReason.add(de.getMessageObject());
+        return false;
       }
     }
 
@@ -977,66 +965,55 @@
   @Override
   public ConfigChangeResult applyConfigurationAdd(BackendCfg cfg)
   {
-    DN                backendDN           = cfg.dn();
-    final ConfigChangeResult ccr = new ConfigChangeResult();
-
-    // Register as a change listener for this backend entry so that we will
-    // be notified of any changes that may be made to it.
-    cfg.addChangeListener(this);
-
-    // See if the entry contains an attribute that indicates whether the backend should be enabled.
-    // If it does not, or if it is not set to "true", then skip it.
-    if (!cfg.isEnabled())
-    {
-      // The backend is explicitly disabled.  We will log a message to
-      // indicate that it won't be enabled and return.
-      LocalizableMessage message = INFO_CONFIG_BACKEND_DISABLED.get(backendDN);
-      logger.debug(message);
-      ccr.addMessage(message);
-      return ccr;
-    }
-
-
-
-    // See if the entry contains an attribute that specifies the backend ID.  If
-    // it does not, then skip it.
-    String backendID = cfg.getBackendId();
-    if (hasLocalBackend(backendID))
-    {
-      LocalizableMessage message = WARN_CONFIG_BACKEND_DUPLICATE_BACKEND_ID.get(backendDN, backendID);
-      logger.warn(message);
-      ccr.addMessage(message);
-      return ccr;
-    }
-
-
-    // See if the entry contains an attribute that specifies the class name
-    // for the backend implementation.  If it does, then load it and make sure
-    // that it's a valid backend implementation.  There is no such attribute,
-    // the specified class cannot be loaded, or it does not contain a valid
-    // backend implementation, then log an error and skip it.
-    String className = cfg.getJavaClass();
-
-    Backend<? extends BackendCfg> backend;
+    final ConfigChangeResult changeResult = new ConfigChangeResult();
+    writeLock.lock();
     try
     {
-      backend = loadBackendClass(className).newInstance();
+      DN backendDN = cfg.dn();
+      cfg.addChangeListener(this);
+
+      if (!cfg.isEnabled())
+      {
+        LocalizableMessage message = INFO_CONFIG_BACKEND_DISABLED.get(backendDN);
+        logger.debug(message);
+        changeResult.addMessage(message);
+        return changeResult;
+      }
+
+      String backendID = cfg.getBackendId();
+      if (hasLocalBackend(backendID))
+      {
+        LocalizableMessage message = WARN_CONFIG_BACKEND_DUPLICATE_BACKEND_ID.get(backendDN, backendID);
+        logger.warn(message);
+        changeResult.addMessage(message);
+        return changeResult;
+      }
+
+      String className = cfg.getJavaClass();
+      Backend<? extends BackendCfg> backend;
+      try
+      {
+        backend = loadBackendClass(className).newInstance();
+      }
+      catch (Exception e)
+      {
+        logger.traceException(e);
+        changeResult.setResultCode(DirectoryServer.getServerErrorResultCode());
+        changeResult.addMessage(
+            ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(className, backendDN, stackTraceToSingleLineString(e)));
+        return changeResult;
+      }
+
+      initializeBackend(backend, cfg, changeResult);
     }
-    catch (Exception e)
+    finally
     {
-      logger.traceException(e);
-
-      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
-      ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(
-          className, backendDN, stackTraceToSingleLineString(e)));
-      return ccr;
+      writeLock.unlock();
     }
-
-    initializeBackend(backend, cfg, ccr);
-    return ccr;
+    return changeResult;
   }
 
-  private boolean configureAndOpenBackend(Backend<?> backend, BackendCfg cfg, ConfigChangeResult ccr)
+  private boolean configureAndOpenBackend(Backend<?> backend, BackendCfg cfg, ConfigChangeResult changeResult)
   {
     try
     {
@@ -1046,11 +1023,9 @@
     catch (Exception e)
     {
       logger.traceException(e);
-
-      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
-      ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INITIALIZE.get(
-          cfg.getJavaClass(), cfg.dn(), stackTraceToSingleLineString(e)));
-
+      changeResult.setResultCode(DirectoryServer.getServerErrorResultCode());
+      changeResult.addMessage(
+          ERR_CONFIG_BACKEND_CANNOT_INITIALIZE.get(cfg.getJavaClass(), cfg.dn(), stackTraceToSingleLineString(e)));
       releaseSharedLock(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend, cfg.getBackendId());
       return false;
     }
@@ -1085,84 +1060,65 @@
   }
 
   @Override
-  public boolean isConfigurationDeleteAcceptable(
-       BackendCfg configEntry,
-       List<LocalizableMessage> unacceptableReason)
+  public boolean isConfigurationDeleteAcceptable(BackendCfg configEntry, List<LocalizableMessage> unacceptableReason)
   {
     DN backendDN = configEntry.dn();
-
-    // 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.  If we
-    // 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);
+    Backend<?> backend = configuredBackends.get(backendDN);
     if (backend == null)
     {
       return true;
     }
-
-    if (backend instanceof LocalBackend)
+    for (DN baseDN : backend.getBaseDNs())
     {
-      // See if the backend has any subordinate backends.  If so, then it is not
-      // acceptable to remove it.  Otherwise, it should be fine.
-      LocalBackend<?>[] subBackends = ((LocalBackend<?>) backend).getSubordinateBackends();
-      if (subBackends != null && subBackends.length != 0)
+      if (registry.subordinates.containsKey(baseDN))
       {
         unacceptableReason.add(NOTE_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES.get(backendDN));
         return false;
       }
-      return true;
     }
-    throw new RuntimeException("isConfigurationDeleteAcceptable() is not yet supported for proxy backend.");
+    return true;
   }
 
   @Override
   public ConfigChangeResult applyConfigurationDelete(BackendCfg configEntry)
   {
-    DN                backendDN           = configEntry.dn();
-    final ConfigChangeResult ccr = new ConfigChangeResult();
-
-    // 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 (backend == null)
-    {
-      return ccr;
-    }
-
-    if (backend instanceof LocalBackend)
-    {
-      // See if the backend has any subordinate backends.  If so, then it is not
-      // acceptable to remove it.  Otherwise, it should be fine.
-      LocalBackend<?>[] subBackends = ((LocalBackend<?>) backend).getSubordinateBackends();
-      if (subBackends != null && subBackends.length > 0)
-      {
-        ccr.setResultCode(UNWILLING_TO_PERFORM);
-        ccr.addMessage(NOTE_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES.get(backendDN));
-        return ccr;
-      }
-    }
-    else
-    {
-      throw new RuntimeException("applyConfigurationDelete() is not yet supported for proxy backend.");
-    }
-
-    deregisterBackend(backendDN, backend);
-
+    final ConfigChangeResult changeResult = new ConfigChangeResult();
+    writeLock.lock();
     try
     {
-      backend.finalizeBackend();
+      DN backendDN = configEntry.dn();
+      Backend<?> backend = configuredBackends.get(backendDN);
+      if (backend == null)
+      {
+        return changeResult;
+      }
+      for (DN baseDN : backend.getBaseDNs())
+      {
+        if (registry.subordinates.containsKey(baseDN))
+        {
+          changeResult.setResultCode(UNWILLING_TO_PERFORM);
+          changeResult.addMessage(NOTE_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES.get(backendDN));
+          return changeResult;
+        }
+      }
+
+      deregisterBackend(backendDN, backend);
+      try
+      {
+        backend.finalizeBackend();
+      }
+      catch (Exception e)
+      {
+        logger.traceException(e);
+      }
+      configEntry.removeChangeListener(this);
+      releaseSharedLock(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend, backend.getBackendID());
     }
-    catch (Exception e)
+    finally
     {
-      logger.traceException(e);
+      writeLock.unlock();
     }
-
-    configEntry.removeChangeListener(this);
-
-    releaseSharedLock(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend, backend.getBackendID());
-
-    return ccr;
+    return changeResult;
   }
 
   private void deregisterBackend(DN backendDN, Backend<?> backend)
@@ -1175,7 +1131,7 @@
         listener.performBackendPreFinalizationProcessing(localBackend);
       }
 
-      registeredBackends.remove(backendDN);
+      configuredBackends.remove(backendDN);
       deregisterLocalBackend(localBackend);
 
       for (LocalBackendInitializationListener listener : initializationListeners)
@@ -1191,627 +1147,656 @@
   /** Shutdown all local backends. */
   public void shutdownLocalBackends()
   {
-    for (LocalBackend<?> backend : localBackends.values())
+    writeLock.lock();
+    try
     {
-      try
+      for (LocalBackend<?> backend : localBackendsById.values())
       {
-        for (LocalBackendInitializationListener listener : initializationListeners)
+        try
         {
-          listener.performBackendPreFinalizationProcessing(backend);
+          for (LocalBackendInitializationListener listener : initializationListeners)
+          {
+            listener.performBackendPreFinalizationProcessing(backend);
+          }
+
+          backend.finalizeBackend();
+
+          for (LocalBackendInitializationListener listener : initializationListeners)
+          {
+            listener.performBackendPostFinalizationProcessing(backend);
+          }
+
+          releaseSharedLock(WARN_SHUTDOWN_CANNOT_RELEASE_SHARED_BACKEND_LOCK, backend, backend.getBackendID());
         }
-
-        backend.finalizeBackend();
-
-        for (LocalBackendInitializationListener listener : initializationListeners)
+        catch (Exception e)
         {
-          listener.performBackendPostFinalizationProcessing(backend);
+          logger.traceException(e);
         }
-
-        releaseSharedLock(WARN_SHUTDOWN_CANNOT_RELEASE_SHARED_BACKEND_LOCK, backend, backend.getBackendID());
       }
-      catch (Exception e)
-      {
-        logger.traceException(e);
-      }
+    }
+    finally
+    {
+      writeLock.unlock();
     }
   }
 
   /**
-   * Registry for maintaining the set of registered base DN's, associated local backends
-   * and naming context information.
+   * The registry maintains the relationships between the base DNs and the backends.
+   * <p>
+   * Implementation note: a separate map is kept for local backends in order to avoid filtering the backends map
+   * each time local backends are requested.
+   * <p>
    */
-  static private class BaseDnRegistry {
-
-    /** The set of base DNs registered with the server. */
-    private final TreeMap<DN, LocalBackend<?>> baseDNs = new TreeMap<>();
-    /** The set of private naming contexts registered with the server. */
-    private final TreeMap<DN, LocalBackend<?>> privateNamingContexts = new TreeMap<>();
-    /** The set of public naming contexts registered with the server. */
-    private final TreeMap<DN, LocalBackend<?>> publicNamingContexts = new TreeMap<>();
-    /** The set of public naming contexts, including sub-suffixes, registered with the server. */
-    private final TreeMap<DN, LocalBackend<?>> allPublicNamingContexts = new TreeMap<>();
-    /** Lock to protect reads of the maps. */
-    private final ReadLock readLock;
-    /** Lock to protect updates of the maps. */
-    private final WriteLock writeLock;
-
+  private static class Registry
+  {
     /**
-     * Indicates whether this base DN registry is in test mode.
-     * A registry instance that is in test mode will not modify backend
-     * objects referred to in the above maps.
+     * The mapping between base DNs and their corresponding local backends.
+     * It is a subset of backendsByName field.
      */
-    private boolean testOnly;
-
+    final Map<DN, LocalBackend<? extends LocalBackendCfg>> localBackendsByName = new HashMap<>();
     /**
-     * Registers a base DN with this registry.
-     *
-     * @param  baseDN to register
-     * @param  backend with which the base DN is associated
-     * @param  isPrivate indicates whether this base DN is private
-     * @return list of error messages generated by registering the base DN
-     *         that should be logged if the changes to this registry are
-     *         committed to the server
-     * @throws DirectoryException if the base DN cannot be registered
+     * The mapping between base DNs and their corresponding backends.
+     * <p>
+     * Note that the pair (root DN, RootDSEBackend) is not included in this mapping.
      */
-    List<LocalizableMessage> registerBaseDN(DN baseDN, LocalBackend<?> backend, boolean isPrivate)
-        throws DirectoryException
+    final Map<DN, Backend<? extends BackendCfg>> backendsByName = new HashMap<>();
+    /**
+     * The map of subordinates relationships between base DNs, which provides for each base DN
+     * the set of its subordinates.
+     * <p>
+     * A base DN with no subordinates does not appear in this map.
+     * Note that the root DN ("") is not included in this mapping.
+     */
+    final Map<DN, Set<DN>> subordinates = new HashMap<>();
+
+    /** The naming contexts, including sub-suffixes. */
+    final Set<NamingContext> namingContexts = new HashSet<>();
+
+    Registry copy()
     {
-      writeLock.lock();
-      try
-      {
-        return registerBaseDN0(baseDN, backend, isPrivate);
-      }
-      finally
-      {
-        writeLock.unlock();
-      }
+      return copy(true);
     }
 
-    private List<LocalizableMessage> registerBaseDN0(DN baseDN, LocalBackend<?> backend, boolean isPrivate)
-        throws DirectoryException
+    Registry copyForCheckingChanges()
+    {
+      return copy(false);
+    }
+
+    Set<DN> getNamingContexts(final NamingContextFilter... filters)
+    {
+      Predicate<NamingContext, Void> predicate = new Predicate<NamingContext, Void>()
+      {
+        @Override
+        public boolean matches(NamingContext namingContext, Void p)
+        {
+          for (NamingContextFilter filter : filters)
+          {
+            if (!filter.matches(namingContext, p))
+            {
+              return false;
+            }
+          }
+          return true;
+        }
+      };
+      Set<DN> result = new HashSet<>();
+      for (NamingContext namingContext : Iterables.filteredIterable(namingContexts, predicate))
+      {
+        result.add(namingContext.getBaseDN());
+      }
+      return result;
+    }
+
+    boolean containsLocalNamingContext(DN dn)
+    {
+      if (localBackendsByName.containsKey(dn))
+      {
+        for (NamingContext name : namingContexts)
+        {
+          if (name.getBaseDN().equals(dn))
+          {
+            return name.isLocal && !name.isSubSuffix;
+          }
+        }
+      }
+      return false;
+    }
+
+    private Registry copy(boolean isUpdate)
+    {
+      Registry registry = isUpdate ? new Registry() : new CheckingRegistry();
+      registry.localBackendsByName.putAll(localBackendsByName);
+      registry.backendsByName.putAll(backendsByName);
+      for (Map.Entry<DN, Set<DN>> entry : subordinates.entrySet())
+      {
+        registry.subordinates.put(entry.getKey(), new HashSet<>(entry.getValue()));
+      }
+      registry.namingContexts.addAll(namingContexts);
+      return registry;
+    }
+
+    void registerBaseDN(DN baseDN, Backend<? extends BackendCfg> backend, boolean isPrivate) throws DirectoryException
     {
       // Check to see if the base DN is already registered with the server.
-      LocalBackend<?> existingBackend = baseDNs.get(baseDN);
+      Backend<?> existingBackend = backendsByName.get(baseDN);
       if (existingBackend != null)
       {
-        LocalizableMessage message = ERR_REGISTER_BASEDN_ALREADY_EXISTS.
-            get(baseDN, backend.getBackendID(), existingBackend.getBackendID());
+        LocalizableMessage message =
+            ERR_REGISTER_BASEDN_ALREADY_EXISTS.get(baseDN, backend.getBackendID(), existingBackend.getBackendID());
         throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
       }
 
       // 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
+      // 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<>();
-      for (DN dn : baseDNs.keySet())
+      List<DN> otherBackendBaseDNs = new ArrayList<>();
+      for (Map.Entry<DN, Backend<? extends BackendCfg>> entry : backendsByName.entrySet())
       {
-        LocalBackend<?> b = baseDNs.get(dn);
+        Backend<?> b = entry.getValue();
         if (b.equals(backend))
         {
-          otherBaseDNs.add(dn);
-
+          DN dn = entry.getKey();
+          otherBackendBaseDNs.add(dn);
           if (baseDN.isSuperiorOrEqualTo(dn) || baseDN.isSubordinateOrEqualTo(dn))
           {
-            LocalizableMessage message = ERR_REGISTER_BASEDN_HIERARCHY_CONFLICT.
-                get(baseDN, backend.getBackendID(), dn);
+            LocalizableMessage message = ERR_REGISTER_BASEDN_HIERARCHY_CONFLICT.get(baseDN, backend.getBackendID(), dn);
             throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
           }
         }
       }
 
       // 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
+      // already defined. If it is, then any other base DN(s) for the same
       // backend must also be subordinate to the same base DN.
-      final LocalBackend<?> superiorBackend = getSuperiorBackend(baseDN, otherBaseDNs, backend.getBackendID());
-      if (superiorBackend == null && backend.getParentBackend() != null)
+      final DN parentBaseDN = retrieveParentBackend(backend.getBackendID(), baseDN, otherBackendBaseDNs);
+      Backend<?> parentBackend = null;
+      if (parentBaseDN == null)
       {
-        LocalizableMessage message = ERR_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE.
-            get(baseDN, backend.getBackendID(), backend.getParentBackend().getBackendID());
-        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+        // check that other base DNs do no have a parent
+        for (DN dn : otherBackendBaseDNs)
+        {
+          DN parentDn = retrieveParentSuffix(dn);
+          if (parentDn != null)
+          {
+            String parentBackendId = backendsByName.get(parentDn).getBackendID();
+            LocalizableMessage message =
+                ERR_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE.get(baseDN, backend.getBackendID(), parentBackendId);
+            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+          }
+        }
+      }
+      else
+      {
+        parentBackend = backendsByName.get(parentBaseDN);
       }
 
-      // Check to see if the new base DN should be the superior base DN for any
-      // other base DN(s) already defined.
-      LinkedList<LocalBackend<?>> subordinateBackends = new LinkedList<>();
-      LinkedList<DN>      subordinateBaseDNs  = new LinkedList<>();
-      for (DN dn : baseDNs.keySet())
+      List<DN> subordinateBaseDNs = new LinkedList<>();
+      for (Map.Entry<DN, Backend<? extends BackendCfg>> entry : backendsByName.entrySet())
       {
-        LocalBackend<?> b = baseDNs.get(dn);
+        DN dn = entry.getKey();
         DN parentDN = dn.parent();
         while (parentDN != null)
         {
           if (parentDN.equals(baseDN))
           {
             subordinateBaseDNs.add(dn);
-            subordinateBackends.add(b);
             break;
           }
-          else if (baseDNs.containsKey(parentDN))
+          else if (backendsByName.containsKey(parentDN))
           {
             break;
           }
-
           parentDN = parentDN.parent();
         }
       }
 
-      // If we've gotten here, then the new base DN is acceptable.  If we should
+      // If we've gotten here, then the new base DN is acceptable. If we should
       // actually apply the changes then do so now.
-      final List<LocalizableMessage> errors = new LinkedList<>();
+      checkEntryInMultipleBackends(baseDN, backend, parentBackend);
 
+      backendsByName.put(baseDN, backend);
+      if (backend instanceof LocalBackend<?>)
+      {
+        LocalBackend<? extends LocalBackendCfg> localBackend = (LocalBackend<? extends LocalBackendCfg>) backend;
+        localBackendsByName.put(baseDN, localBackend);
+        setPrivateBackend(isPrivate, parentBackend, localBackend);
+      }
+
+      namingContexts.add(new NamingContext(baseDN, isPrivate, (parentBackend!=null), backend instanceof LocalBackend));
+      if (parentBaseDN != null)
+      {
+        addSubordinateDn(parentBaseDN, baseDN);
+        for (DN subordinateDN : subordinateBaseDNs)
+        {
+          removeSubordinateDn(parentBaseDN, subordinateDN);
+        }
+      }
+      for (DN subDN : subordinateBaseDNs)
+      {
+        addSubordinateDn(baseDN, subDN);
+        switchNamingContextIsSubSuffix(subDN);
+      }
+    }
+
+    void setPrivateBackend(boolean isPrivate, final Backend<?> parentBackend,
+        LocalBackend<? extends LocalBackendCfg> localBackend)
+    {
+      if (parentBackend == null)
+      {
+        localBackend.setPrivateBackend(isPrivate);
+      }
+    }
+
+    void checkEntryInMultipleBackends(DN baseDN, Backend<? extends BackendCfg> backend,
+        final Backend<?> parentBackend) throws DirectoryException
+    {
       // 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
+      // 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
+      // 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 (parentBackend != null)
       {
-        if (superiorBackend.entryExists(baseDN))
+        if (entryExistsInBackend(baseDN, parentBackend))
         {
-          errors.add(WARN_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS.
-              get(superiorBackend.getBackendID(), baseDN, backend.getBackendID()));
+          logger.error(WARN_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS.get(
+              parentBackend.getBackendID(), baseDN, backend.getBackendID()));
         }
       }
-
-      baseDNs.put(baseDN, backend);
-
-      if (superiorBackend == null)
-      {
-        if (!testOnly)
-        {
-          backend.setPrivateBackend(isPrivate);
-        }
-
-        if (isPrivate)
-        {
-          privateNamingContexts.put(baseDN, backend);
-        }
-        else
-        {
-          publicNamingContexts.put(baseDN, backend);
-        }
-      }
-      else if (otherBaseDNs.isEmpty() && !testOnly)
-      {
-        backend.setParentBackend(superiorBackend);
-        superiorBackend.addSubordinateBackend(backend);
-      }
-
-      if (!testOnly)
-      {
-        for (LocalBackend<?> b : subordinateBackends)
-        {
-          LocalBackend<?> oldParentBackend = b.getParentBackend();
-          if (oldParentBackend != null)
-          {
-            oldParentBackend.removeSubordinateBackend(b);
-          }
-
-          b.setParentBackend(backend);
-          backend.addSubordinateBackend(b);
-        }
-      }
-
-      if (!isPrivate)
-      {
-        allPublicNamingContexts.put(baseDN, backend);
-      }
-      for (DN dn : subordinateBaseDNs)
-      {
-        publicNamingContexts.remove(dn);
-        privateNamingContexts.remove(dn);
-      }
-
-      return errors;
-    }
-
-    LocalBackend<?> getBackendWithBaseDN(DN entryDN)
-    {
-      readLock.lock();
-      try
-      {
-        return baseDNs.get(entryDN);
-      }
-      finally
-      {
-        readLock.unlock();
-      }
     }
 
-
-    BackendAndName getBackendAndName(final DN entryDN)
+    DN findNamingContextForEntry(final DN entryDN)
     {
-      readLock.lock();
-      try
+      if (entryDN.isRootDN())
       {
-        /*
-         * Try to minimize the number of lookups in the map to find the backend containing the entry.
-         * 1) If the DN contains many RDNs it is faster to iterate through the list of registered backends,
-         * 2) Otherwise iterating through the parents requires less lookups. It also avoids some attacks
-         * where we would spend time going through the list of all parents to finally decide the
-         * baseDN is absent.
-         */
-        if (entryDN.size() <= baseDNs.size())
+        return entryDN;
+      }
+      /*
+       * Try to minimize the number of lookups in the map to find the backend containing the entry.
+       * 1) If the DN contains many RDNs it is faster to iterate through the list of registered backends,
+       * 2) Otherwise iterating through the parents requires less lookups. It also avoids some attacks
+       * where we would spend time going through the list of all parents to finally decide the baseDN
+       * is absent.
+       */
+      if (entryDN.size() <= backendsByName.size())
+      {
+        DN matchedDN = entryDN;
+        while (!matchedDN.isRootDN())
         {
-          DN matchedDN = entryDN;
-          while (!matchedDN.isRootDN())
+          if (backendsByName.containsKey(matchedDN))
           {
-            final LocalBackend<?> backend = baseDNs.get(matchedDN);
-            if (backend != null)
-            {
-              return new BackendAndName(backend, matchedDN);
-            }
-            matchedDN = matchedDN.parent();
+            return matchedDN;
           }
-          return null;
+          matchedDN = matchedDN.parent();
         }
-        else
-        {
-          LocalBackend<?> backend = null;
-          DN matchedDN = null;
-          int currentSize = 0;
-          for (DN backendDN : baseDNs.keySet())
-          {
-            if (entryDN.isSubordinateOrEqualTo(backendDN) && backendDN.size() > currentSize)
-            {
-              backend = baseDNs.get(backendDN);
-              matchedDN = backendDN;
-              currentSize = backendDN.size();
-            }
-          }
-          return new BackendAndName(backend, matchedDN);
-        }
-      }
-      finally
-      {
-        readLock.unlock();
-      }
-    }
-
-    private LocalBackend<?> getSuperiorBackend(DN baseDN, LinkedList<DN> otherBaseDNs, String backendID)
-        throws DirectoryException
-    {
-      readLock.lock();
-      try
-      {
-        LocalBackend<?> superiorBackend = null;
-        DN parentDN = baseDN.parent();
-        while (parentDN != null)
-        {
-          if (baseDNs.containsKey(parentDN))
-          {
-            superiorBackend = baseDNs.get(parentDN);
-
-            for (DN dn : otherBaseDNs)
-            {
-              if (!dn.isSubordinateOrEqualTo(parentDN))
-              {
-                LocalizableMessage msg = ERR_REGISTER_BASEDN_DIFFERENT_PARENT_BASES.get(baseDN, backendID, dn);
-                throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg);
-              }
-            }
-            break;
-          }
-
-          parentDN = parentDN.parent();
-        }
-        return superiorBackend;
-      }
-      finally
-      {
-        readLock.unlock();
-      }
-    }
-
-    /**
-     * Deregisters a base DN with this registry.
-     *
-     * @param  baseDN to deregister
-     * @return list of error messages generated by deregistering the base DN
-     *         that should be logged if the changes to this registry are
-     *         committed to the server
-     * @throws DirectoryException if the base DN could not be deregistered
-     */
-     List<LocalizableMessage> deregisterBaseDN(DN baseDN) throws DirectoryException
-     {
-       writeLock.lock();
-       try
-       {
-         return deregisterBaseDN0(baseDN);
-       }
-       finally
-       {
-         writeLock.unlock();
-       }
-     }
-
-    List<LocalizableMessage> deregisterBaseDN0(DN baseDN) throws DirectoryException
-    {
-      ifNull(baseDN);
-
-      // Make sure that the Directory Server actually contains a backend with
-      // the specified base DN.
-      LocalBackend<?> backend = baseDNs.get(baseDN);
-      if (backend == null)
-      {
-        LocalizableMessage message =
-            ERR_DEREGISTER_BASEDN_NOT_REGISTERED.get(baseDN);
-        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
-      }
-
-      // 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.
-      LocalBackend<?> superiorBackend = backend.getParentBackend();
-      LinkedList<LocalBackend<?>> subordinateBackends = new LinkedList<>();
-      if (backend.getSubordinateBackends() != null)
-      {
-        for (LocalBackend<?> b : backend.getSubordinateBackends())
-        {
-          for (DN dn : b.getBaseDNs())
-          {
-            if (dn.isSubordinateOrEqualTo(baseDN))
-            {
-              subordinateBackends.add(b);
-              break;
-            }
-          }
-        }
-      }
-
-      // See if there are any other base DNs registered within the same backend.
-      LinkedList<DN> otherBaseDNs = new LinkedList<>();
-      for (DN dn : baseDNs.keySet())
-      {
-        if (dn.equals(baseDN))
-        {
-          continue;
-        }
-
-        LocalBackend<?> b = baseDNs.get(dn);
-        if (backend.equals(b))
-        {
-          otherBaseDNs.add(dn);
-        }
-      }
-
-      // If we've gotten here, then it's OK to make the changes.
-
-      // Get rid of the references to this base DN in the mapping tree
-      // information.
-      baseDNs.remove(baseDN);
-      publicNamingContexts.remove(baseDN);
-      allPublicNamingContexts.remove(baseDN);
-      privateNamingContexts.remove(baseDN);
-
-      final LinkedList<LocalizableMessage> errors = new LinkedList<>();
-      if (superiorBackend == null)
-      {
-        // If there were any subordinate backends, then all of their base DNs
-        // will now be promoted to naming contexts.
-        for (LocalBackend<?> b : subordinateBackends)
-        {
-          if (!testOnly)
-          {
-            b.setParentBackend(null);
-            backend.removeSubordinateBackend(b);
-          }
-
-          for (DN dn : b.getBaseDNs())
-          {
-            if (b.isPrivateBackend())
-            {
-              privateNamingContexts.put(dn, b);
-            }
-            else
-            {
-              publicNamingContexts.put(dn, b);
-            }
-          }
-        }
+        return null;
       }
       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() && !testOnly)
+        DN matchedDN = null;
+        int currentSize = 0;
+        for (DN backendDN : backendsByName.keySet())
         {
-          superiorBackend.removeSubordinateBackend(backend);
+          if (entryDN.isSubordinateOrEqualTo(backendDN) && backendDN.size() > currentSize)
+          {
+            matchedDN = backendDN;
+            currentSize = backendDN.size();
+          }
         }
+        return matchedDN;
+      }
+    }
+
+    /** Returns the parent naming context for base DN, or {@code null} if there is no parent. */
+    private DN retrieveParentBackend(String backendID, DN baseDN, List<DN> otherBackendBaseDNs)
+        throws DirectoryException
+    {
+      DN parentDN = baseDN.parent();
+      while (parentDN != null)
+      {
+        if (backendsByName.containsKey(parentDN))
+        {
+          break;
+        }
+        parentDN = parentDN.parent();
+      }
+
+      if (parentDN == null) {
+        return null;
+      }
+      // check that other base DNs of the backend have the same subordinate
+      for (DN dn : otherBackendBaseDNs)
+      {
+        if (!dn.isSubordinateOrEqualTo(parentDN))
+        {
+          LocalizableMessage msg = ERR_REGISTER_BASEDN_DIFFERENT_PARENT_BASES.get(baseDN, backendID, dn);
+          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg);
+        }
+      }
+      return parentDN;
+    }
+
+    /** Returns the DN that has the provided DN as a subordinate, or {@code null} if there is no parent. */
+    private DN retrieveParentSuffix(DN dn)
+    {
+      for (Map.Entry<DN, Set<DN>> entry : subordinates.entrySet())
+      {
+        Set<DN> subs = entry.getValue();
+        if (subs.contains(dn))
+        {
+          return entry.getKey();
+        }
+      }
+      return null;
+    }
+
+    private boolean entryExistsInBackend(DN dn, Backend<?> backend) throws DirectoryException
+    {
+      if (backend instanceof LocalBackend<?>)
+      {
+        return ((LocalBackend<?>) backend).entryExists(dn);
+      }
+      // TODO: assume entry exists in non-local backend, Is it correct ?
+      return true;
+    }
+
+    private void addSubordinateDn(DN dn, DN subordinateDn)
+    {
+      Set<DN> subs = subordinates.get(dn);
+      if (subs == null)
+      {
+        subs = new HashSet<>();
+        subordinates.put(dn, subs);
+      }
+      subs.add(subordinateDn);
+    }
+
+    void deregisterBaseDN(DN baseDN) throws DirectoryException
+    {
+      Reject.ifNull(baseDN);
+
+      Backend<?> backend = backendsByName.get(baseDN);
+      if (backend == null)
+      {
+        LocalizableMessage message = ERR_DEREGISTER_BASEDN_NOT_REGISTERED.get(baseDN);
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+      }
+
+      backendsByName.remove(baseDN);
+      if (backend instanceof LocalBackend<?>)
+      {
+        localBackendsByName.remove(baseDN);
+      }
+      removeNamingContext(baseDN);
+
+      Set<DN> subordinatesDNs = getSubordinateNamingContexts(baseDN);
+      subordinates.remove(baseDN);
+      DN parentDN = retrieveParentSuffix(baseDN);
+      if (parentDN == null)
+      {
+        // If there were any subordinate naming contexts,
+        // they are all promoted to top-level naming contexts.
+        for (DN dn : subordinatesDNs)
+          {
+            switchNamingContextIsSubSuffix(dn);
+          }
+      }
+      else
+      {
+        removeSubordinateDn(parentDN, baseDN);
 
         // If there are any subordinate backends, then they need to be made
-        // subordinate to the parent backend.  Also, we should log a warning
+        // 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())
+        if (!subordinatesDNs.isEmpty())
         {
-          // Suppress this warning message on server shutdown.
-          if (!DirectoryServer.getInstance().isShuttingDown()) {
-            errors.add(WARN_DEREGISTER_BASEDN_MISSING_HIERARCHY.get(
-                baseDN, backend.getBackendID()));
-          }
-
-          if (!testOnly)
+          checkMissingHierarchy(baseDN, backend);
+          for (DN subDN : subordinatesDNs)
           {
-            for (LocalBackend<?> b : subordinateBackends)
-            {
-              backend.removeSubordinateBackend(b);
-              superiorBackend.addSubordinateBackend(b);
-              b.setParentBackend(superiorBackend);
-            }
+            addSubordinateDn(parentDN, subDN);
           }
         }
       }
-      return errors;
     }
 
-    /** Creates a default instance. */
-    BaseDnRegistry()
+    void checkMissingHierarchy(DN baseDN, Backend<?> backend)
     {
-      this(false);
-    }
-
-    /**
-     * Returns a copy of this registry.
-     *
-     * @return copy of this registry
-     */
-    BaseDnRegistry copy()
-    {
-      readLock.lock();
-      try
+      if (!DirectoryServer.getInstance().isShuttingDown())
       {
-        final BaseDnRegistry registry = new BaseDnRegistry(true);
-        registry.baseDNs.putAll(baseDNs);
-        registry.publicNamingContexts.putAll(publicNamingContexts);
-        registry.allPublicNamingContexts.putAll(allPublicNamingContexts);
-        registry.privateNamingContexts.putAll(privateNamingContexts);
-        return registry;
-      }
-      finally
-      {
-        readLock.unlock();
+        logger.error(WARN_DEREGISTER_BASEDN_MISSING_HIERARCHY.get(baseDN, backend.getBackendID()));
       }
     }
 
-    /**
-     * Creates a parameterized instance.
-     *
-     * @param testOnly indicates whether this registry will be used for testing;
-     *        when <code>true</code> this registry will not modify backends
-     */
-    private BaseDnRegistry(boolean testOnly)
+    Set<Backend<?>> getSubordinateBackends(DN baseDN)
     {
-      this.testOnly = testOnly;
-      ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
-      readLock = lock.readLock();
-      writeLock = lock.writeLock();
+      final Set<Backend<?>> backends = new HashSet<>();
+      if (baseDN.isRootDN())
+      {
+        for (DN dn : getNamingContexts(PUBLIC, TOP_LEVEL))
+        {
+          backends.add(backendsByName.get(dn));
+        }
+        return backends;
+      }
+      // general case
+      Set<DN> subDNs = subordinates.get(baseDN);
+      if (subDNs != null)
+      {
+        for (DN subDN : subDNs)
+        {
+          Backend<? extends BackendCfg> backend = backendsByName.get(subDN);
+          if (backend != null)
+          {
+            // it is safe to add same backend several times because it is a set
+            backends.add(backend);
+          }
+        }
+      }
+      return backends;
     }
 
-    /**
-     * Gets the mapping of registered public naming contexts, not including
-     * sub-suffixes, to their associated backend.
-     *
-     * @return mapping from naming context to backend
-     */
-    Map<DN, LocalBackend<?>> getPublicNamingContextsMap()
+    Set<DN> getSubordinateNamingContexts(DN baseDN)
     {
-      readLock.lock();
-      try
+      final Set<DN> dns = new HashSet<>();
+      if (baseDN.isRootDN())
       {
-        return this.publicNamingContexts;
+        for (DN dn : getNamingContexts(PUBLIC, TOP_LEVEL))
+        {
+          dns.add(dn);
+        }
+        return dns;
       }
-      finally
+      // general case
+      Set<DN> subDNs = subordinates.get(baseDN);
+      if (subDNs != null)
       {
-        readLock.unlock();
+        dns.addAll(subDNs);
+      }
+      return dns;
+    }
+
+    Set<DN> getSubordinateLocalNamingContexts(DN baseDN)
+    {
+      Set<DN> subs = new HashSet<>();
+      for (DN subDN : getSubordinateNamingContexts(baseDN))
+      {
+        if (localBackendsByName.containsKey(subDN))
+        {
+          subs.add(subDN);
+        }
+      }
+      return subs;
+    }
+
+    private void removeSubordinateDn(DN dn, DN subordinateDn)
+    {
+      if (subordinates.containsKey(dn))
+      {
+        Set<DN> subs = subordinates.get(dn);
+        subs.remove(subordinateDn);
+        if (subs.isEmpty())
+        {
+          subordinates.remove(dn);
+        }
       }
     }
 
-    /**
-     * Gets the mapping of registered public naming contexts, including sub-suffixes,
-     * to their associated backend.
-     *
-     * @return mapping from naming context to backend
-     */
-    Map<DN, LocalBackend<?>> getAllPublicNamingContextsMap()
+    private NamingContext removeNamingContext(DN baseDn)
     {
-      readLock.lock();
-      try
+      for (Iterator<NamingContext> it = namingContexts.iterator(); it.hasNext();)
       {
-        return this.allPublicNamingContexts;
+        NamingContext nc = it.next();
+        if (nc.getBaseDN().equals(baseDn))
+        {
+          it.remove();
+          return nc;
+        }
       }
-      finally
-      {
-        readLock.unlock();
-      }
+      return null;
     }
 
-    /**
-     * Gets the mapping of registered private naming contexts to their
-     * associated backend.
-     *
-     * @return mapping from naming context to backend
-     */
-    Map<DN, LocalBackend<?>> getPrivateNamingContextsMap()
+    private void switchNamingContextIsSubSuffix(DN baseDn)
     {
-      readLock.lock();
-      try
-      {
-        return this.privateNamingContexts;
-      }
-      finally
-      {
-        readLock.unlock();
-      }
+      NamingContext nc = removeNamingContext(baseDn);
+      namingContexts.add(new NamingContext(nc.getBaseDN(), nc.isPrivate(), !nc.isSubSuffix(), nc.isLocal()));
+    }
+  }
+
+  /**
+   * A registry to check that changes on baseDNs are acceptable.
+   * <p>
+   * This registry will only change its internal state. It will not log warning and will not change
+   * state of backends.
+   */
+  private static class CheckingRegistry extends Registry
+  {
+    @Override
+    void setPrivateBackend(boolean isPrivate, final Backend<?> parentBackend,
+        LocalBackend<? extends LocalBackendCfg> localBackend)
+    {
+      // no-op
     }
 
-    /**
-     * Indicates whether the specified DN is contained in this registry as
-     * a naming contexts.
-     *
-     * @param  dn  The DN for which to make the determination.
-     *
-     * @return  {@code true} if the specified DN is a naming context in this
-     *          registry, or {@code false} if it is not.
-     */
-    boolean containsNamingContext(DN dn)
+    @Override
+    void checkEntryInMultipleBackends(DN baseDN, Backend<? extends BackendCfg> backend,
+        final Backend<?> parentBackend) throws DirectoryException
     {
-      readLock.lock();
-      try
+      // no-op
+    }
+
+    @Override
+    void checkMissingHierarchy(DN baseDN, Backend<?> backend)
+    {
+      // no-op
+    }
+  }
+
+  /** Filter on naming context. */
+  public enum NamingContextFilter implements com.forgerock.opendj.util.Predicate<NamingContext, Void>
+  {
+    /** a naming context corresponding to a private backend. */
+    PRIVATE
+    {
+      @Override
+      public boolean matches(NamingContext context, Void p)
       {
-        return privateNamingContexts.containsKey(dn) || publicNamingContexts.containsKey(dn);
+        return context.isPrivate();
       }
-      finally
+    },
+    /** a public naming context (strict opposite of private). */
+    PUBLIC
+    {
+      @Override
+      public boolean matches(NamingContext context, Void p)
       {
-        readLock.unlock();
+        return !context.isPrivate();
+      }
+    },
+    /** a top-level naming context, which is not a subordinate of another naming context. */
+    TOP_LEVEL
+    {
+      @Override
+      public boolean matches(NamingContext context, Void p)
+      {
+        return !context.isSubSuffix();
+      }
+    },
+    /** a naming suffix corresponding to a local backend. */
+    LOCAL
+    {
+      @Override
+      public boolean matches(NamingContext context, Void p)
+      {
+        return context.isLocal();
       }
     }
   }
 
   /**
-   * Holder for a backend and a single base DN managed by the backend.
+   * Represents a naming context, determined by its base DN.
    * <p>
-   * A backend can manages multiple base DNs, this class allow to keep the association for a single DN.
+   * A naming context may be private or public, top-level or sub-suffix, local or non-local.
+   * <p>
+   * This class provides predicates to be able to filter on a provided set of naming contexts.
    */
-  public static class BackendAndName
+  public static class NamingContext
   {
-    private final LocalBackend<?> backend;
-    private final DN baseDn;
+    private final DN baseDN;
+    private final boolean isPrivate;
+    private final boolean isSubSuffix;
+    private final boolean isLocal;
 
-    /**
-     * Creates a new holder for base DN and the backend that manages it.
-     * @param backend
-     *          The backend that holds the base DN
-     * @param baseDn
-     *          A base DN of the backend
-     */
-    public BackendAndName(LocalBackend<?> backend, DN baseDn)
+    NamingContext(DN baseDN, boolean isPrivate, boolean isSubSuffix, boolean isLocal)
     {
-      this.backend = backend;
-      this.baseDn = baseDn;
+      this.baseDN = baseDN;
+      this.isPrivate = isPrivate;
+      this.isSubSuffix = isSubSuffix;
+      this.isLocal = isLocal;
     }
 
     /**
-     * Returns the base DN.
+     * Retrieves the base DN of the naming context.
      *
-     * @return the base DN
+     * @return the baseDN
      */
-    public DN getBaseDn()
+    public DN getBaseDN()
     {
-      return baseDn;
+      return baseDN;
     }
 
     /**
-     * Returns the backend.
+     * Indicates whether this naming context corresponds to a private backend.
      *
-     * @return the backend that holds the base DN
+     * @return {@code true} if naming context is private, {@code false} otherwise
      */
-    public LocalBackend<?> getBackend()
+    public boolean isPrivate()
     {
-      return backend;
+      return isPrivate;
+    }
+
+    /**
+     * Indicates whether this naming context corresponds to a sub-suffix (a non top-level naming context).
+     *
+     * @return {@code true} if naming context is a sub-suffix, {@code false} otherwise
+     */
+    public boolean isSubSuffix()
+    {
+      return isSubSuffix;
+    }
+
+    /**
+     * Indicates whether this naming context corresponds to a local backend.
+     *
+     * @return {@code true} if naming context is local, {@code false} otherwise
+     */
+    public boolean isLocal()
+    {
+      return isLocal;
     }
   }
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java b/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
index f32ee0a..748cd8c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -110,7 +110,6 @@
 import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
 import org.opends.server.api.plugin.PluginResult;
 import org.opends.server.api.plugin.PluginType;
-import org.opends.server.backends.RootDSEBackend;
 import org.opends.server.config.AdministrationConnector;
 import org.opends.server.config.ConfigurationHandler;
 import org.opends.server.config.JMXMBean;
@@ -3347,83 +3346,6 @@
   }
 
   /**
-   * 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, LocalBackend<?>> getPublicNamingContexts()
-  {
-    return directoryServer.backendConfigManager.getPublicNamingContexts();
-  }
-
-  /**
-   * Retrieves the set of public naming contexts, including sub-suffixes,
-   * 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, LocalBackend<?>> getAllPublicNamingContexts()
-  {
-    return directoryServer.backendConfigManager.getAllPublicNamingContexts();
-  }
-
-  /**
-   * 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)
-  {
-    return directoryServer.backendConfigManager.containsNamingContext(dn);
-  }
-
-  /**
-   * Retrieves the DN that is the immediate parent for this DN. This method does take the server's
-   * naming context configuration into account, so if the current DN is a naming context for the
-   * server, then it will not be considered to have a parent.
-   *
-   * @param dn
-   *          the
-   * @return The DN that is the immediate parent for this DN, or {@code null} if this DN does not
-   *         have a parent (either because there is only a single RDN component or because this DN
-   *         is a suffix defined in the server).
-   */
-  public static DN getParentDNInSuffix(DN dn)
-  {
-    if (dn.size() <= 1 || DirectoryServer.isNamingContext(dn))
-    {
-      return null;
-    }
-    return dn.parent();
-  }
-
-  /**
-   * Retrieves the root DSE entry for the Directory Server.
-   *
-   * @return  The root DSE entry for the Directory Server.
-   */
-  public static Entry getRootDSE()
-  {
-    return getRootDSEBackend().getRootDSE();
-  }
-
-  /**
-   * Retrieves the root DSE backend for the Directory Server.
-   *
-   * @return  The root DSE backend for the Directory Server.
-   */
-  public static RootDSEBackend getRootDSEBackend()
-  {
-    return directoryServer.backendConfigManager.getRootDSEBackend();
-  }
-
-  /**
    * Retrieves the DN of the entry containing the server schema definitions.
    *
    * @return  The DN of the entry containing the server schema definitions, or
@@ -3463,7 +3385,7 @@
     {
       return directoryServer.backendConfigManager.getRootDSEBackend().getRootDSE();
     }
-    final LocalBackend<?> backend = directoryServer.backendConfigManager.getLocalBackend(entryDN);
+    final LocalBackend<?> backend = directoryServer.backendConfigManager.findLocalBackendForEntry(entryDN);
     return backend != null ? backend.getEntry(entryDN) : null;
   }
 
@@ -3490,7 +3412,7 @@
 
     // Ask the appropriate backend if the entry exists.
     // If it is not appropriate for any backend, then return false.
-    LocalBackend<?> backend = directoryServer.backendConfigManager.getLocalBackend(entryDN);
+    LocalBackend<?> backend = directoryServer.backendConfigManager.findLocalBackendForEntry(entryDN);
     return backend != null && backend.entryExists(entryDN);
   }
 
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/EntryCacheConfigManager.java b/opendj-server-legacy/src/main/java/org/opends/server/core/EntryCacheConfigManager.java
index 6af2171..1a5cced 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/EntryCacheConfigManager.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/EntryCacheConfigManager.java
@@ -96,7 +96,7 @@
     try
     {
       DefaultEntryCache defaultCache = new DefaultEntryCache();
-      defaultCache.initializeEntryCache(null);
+      defaultCache.initializeEntryCache(serverContext, null);
       DirectoryServer.setEntryCache(defaultCache);
       _defaultEntryCache = defaultCache;
     }
@@ -530,7 +530,7 @@
 
       if (initialize)
       {
-        cache.initializeEntryCache(configuration);
+        cache.initializeEntryCache(serverContext, configuration);
       }
       // This will check if configuration is acceptable on disabled
       // and uninitialized cache instance that has no "acceptable"
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationBasis.java
index 2f3a666..64ee726 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationBasis.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationBasis.java
@@ -518,7 +518,8 @@
       {
         if (getEntryDN() != null)
         {
-          parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+          parentDN = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+              .getParentDNInSuffix(entryDN);
         }
       }
       else
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/PersistentSearch.java b/opendj-server-legacy/src/main/java/org/opends/server/core/PersistentSearch.java
index fe0b1cb..d30ed9f 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/PersistentSearch.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/PersistentSearch.java
@@ -245,7 +245,8 @@
     case BASE_OBJECT:
       return baseDN.equals(dn);
     case SINGLE_LEVEL:
-      return baseDN.equals(DirectoryServer.getParentDNInSuffix(dn));
+      return baseDN.equals(DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getParentDNInSuffix(dn));
     case WHOLE_SUBTREE:
       return baseDN.isSuperiorOrEqualTo(dn);
     case SUBORDINATES:
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java b/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
index d5e9ed2..1fc9af1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
@@ -427,7 +427,7 @@
   private TrustStoreBackend getTrustStoreBackend()
        throws ConfigException
   {
-    LocalBackend<?> b = serverContext.getBackendConfigManager().getLocalBackend(ID_ADS_TRUST_STORE_BACKEND);
+    LocalBackend<?> b = serverContext.getBackendConfigManager().getLocalBackendById(ID_ADS_TRUST_STORE_BACKEND);
     if (b == null)
     {
       throw new ConfigException(ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_NOT_ENABLED.get(ID_ADS_TRUST_STORE_BACKEND));
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/DefaultEntryCache.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/DefaultEntryCache.java
index 981baf4..8fd4b7d 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/DefaultEntryCache.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/DefaultEntryCache.java
@@ -30,6 +30,7 @@
 import org.opends.server.api.EntryCache;
 import org.opends.server.api.MonitorData;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ServerContext;
 import org.forgerock.opendj.ldap.DN;
 import org.opends.server.types.Entry;
 import org.opends.server.types.InitializationException;
@@ -67,7 +68,7 @@
   }
 
   @Override
-  public void initializeEntryCache(EntryCacheCfg configEntry)
+  public void initializeEntryCache(ServerContext serverContext, EntryCacheCfg configEntry)
          throws ConfigException, InitializationException
   {
     // No implementation required.
@@ -223,14 +224,6 @@
   }
 
   @Override
-  public void clearSubtree(DN baseDN)
-  {
-    for (EntryCache<?> entryCache : cacheOrder) {
-      entryCache.clearSubtree(baseDN);
-    }
-  }
-
-  @Override
   public void handleLowMemory()
   {
     for (EntryCache<?> entryCache : cacheOrder) {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/ExactMatchIdentityMapper.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/ExactMatchIdentityMapper.java
index b53c32d..5030cb0 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/ExactMatchIdentityMapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/ExactMatchIdentityMapper.java
@@ -46,6 +46,8 @@
 import org.opends.server.types.*;
 
 import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.util.CollectionUtils.*;
 
@@ -105,7 +107,8 @@
     Set<DN> cfgBaseDNs = configuration.getMatchBaseDN();
     if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
     {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      cfgBaseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     BackendConfigManager backendConfigManager =
@@ -114,7 +117,7 @@
     {
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           throw new ConfigException(ERR_EXACTMAP_ATTR_UNINDEXED.get(
@@ -182,7 +185,8 @@
     Collection<DN> baseDNs = config.getMatchBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     SearchResultEntry matchingEntry = null;
@@ -268,18 +272,18 @@
     // Make sure that all of the configured attributes are indexed for equality
     // in all appropriate backends.
     Set<DN> cfgBaseDNs = configuration.getMatchBaseDN();
-    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
-    {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
-    }
-
     BackendConfigManager backendConfigManager =
         DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
+    {
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
+    }
+
     for (AttributeType t : configuration.getMatchAttribute())
     {
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           unacceptableReasons.add(ERR_EXACTMAP_ATTR_UNINDEXED.get(
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/FIFOEntryCache.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/FIFOEntryCache.java
index 12aeb51..d2200e9 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/FIFOEntryCache.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/FIFOEntryCache.java
@@ -39,10 +39,10 @@
 import org.forgerock.opendj.config.server.ConfigurationChangeListener;
 import org.forgerock.opendj.server.config.server.EntryCacheCfg;
 import org.forgerock.opendj.server.config.server.FIFOEntryCacheCfg;
-import org.opends.server.api.LocalBackend;
 import org.opends.server.api.EntryCache;
 import org.opends.server.api.MonitorData;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ServerContext;
 import org.opends.server.types.CacheEntry;
 import org.opends.server.types.Entry;
 import org.opends.server.types.InitializationException;
@@ -109,6 +109,8 @@
   /** The maximum length of time to try to obtain a lock before giving up. */
   private long lockTimeout = 2000;
 
+  private ServerContext serverContext;
+
   /** Creates a new instance of this FIFO entry cache. */
   public FIFOEntryCache()
   {
@@ -117,9 +119,10 @@
   }
 
   @Override
-  public void initializeEntryCache(FIFOEntryCacheCfg configuration)
+  public void initializeEntryCache(ServerContext serverContext, FIFOEntryCacheCfg configuration)
       throws ConfigException, InitializationException
   {
+    this.serverContext = serverContext;
     registeredConfiguration = configuration;
     configuration.addFIFOChangeListener (this);
 
@@ -596,107 +599,6 @@
   }
 
   @Override
-  public void clearSubtree(DN baseDN)
-  {
-    // Determine which backend should be used for the provided base DN.  If
-    // there is none, then we don't need to do anything.
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(baseDN);
-    if (backend == null)
-    {
-      return;
-    }
-
-    // Acquire a lock on the cache.  We should not return until the cache has
-    // been cleared, so we will block until we can obtain the lock.
-    cacheWriteLock.lock();
-
-    // At this point, it is absolutely critical that we always release the lock
-    // before leaving this method, so do so in a finally block.
-    try
-    {
-      clearSubtree(baseDN, backend);
-    }
-    catch (Exception e)
-    {
-      logger.traceException(e);
-
-      // This shouldn't happen, but there's not much that we can do if it does.
-    }
-    finally
-    {
-      cacheWriteLock.unlock();
-    }
-  }
-
-  /**
-   * Clears all entries at or below the specified base DN that are associated
-   * with the given backend.  The caller must already hold the cache lock.
-   *
-   * @param  baseDN   The base DN below which all entries should be flushed.
-   * @param  backend  The backend for which to remove the appropriate entries.
-   */
-  private void clearSubtree(DN baseDN, LocalBackend<?> backend)
-  {
-    // See if there are any entries for the provided backend in the cache.  If
-    // not, then return.
-    Map<Long,CacheEntry> map = idMap.get(backend.getBackendID());
-    if (map == null)
-    {
-      // No entries were in the cache for this backend, so we can return without
-      // doing anything.
-      return;
-    }
-
-    // Since the provided base DN could hold a subset of the information in the
-    // specified backend, we will have to do this by iterating through all the
-    // entries for that backend.  Since this could take a while, we'll
-    // periodically release and re-acquire the lock in case anyone else is
-    // waiting on it so this doesn't become a stop-the-world event as far as the
-    // cache is concerned.
-    int entriesExamined = 0;
-    Iterator<CacheEntry> iterator = map.values().iterator();
-    while (iterator.hasNext())
-    {
-      CacheEntry e = iterator.next();
-      DN entryDN = e.getEntry().getName();
-      if (entryDN.isSubordinateOrEqualTo(baseDN))
-      {
-        iterator.remove();
-        dnMap.remove(entryDN);
-      }
-
-      entriesExamined++;
-      if ((entriesExamined % 1000) == 0)
-      {
-        cacheWriteLock.unlock();
-        Thread.yield();
-        cacheWriteLock.lock();
-      }
-    }
-
-    // See if the backend has any subordinate backends.  If so, then process
-    // them recursively.
-    for (LocalBackend<?> subBackend : backend.getSubordinateBackends())
-    {
-      boolean isAppropriate = false;
-      for (DN subBase : subBackend.getBaseDNs())
-      {
-        if (subBase.isSubordinateOrEqualTo(baseDN))
-        {
-          isAppropriate = true;
-          break;
-        }
-      }
-
-      if (isAppropriate)
-      {
-        clearSubtree(baseDN, subBackend);
-      }
-    }
-  }
-
-  @Override
   public void handleLowMemory()
   {
     // Grab the lock on the cache and wait until we have it.
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/FingerprintCertificateMapper.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/FingerprintCertificateMapper.java
index 14c421b..23c7a2e 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/FingerprintCertificateMapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/FingerprintCertificateMapper.java
@@ -53,6 +53,8 @@
 import org.opends.server.types.SearchResultEntry;
 
 import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.util.CollectionUtils.*;
@@ -113,7 +115,8 @@
     Set<DN> cfgBaseDNs = configuration.getUserBaseDN();
     if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
     {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      cfgBaseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     AttributeType t = configuration.getFingerprintAttribute();
@@ -121,7 +124,7 @@
         DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     for (DN baseDN : cfgBaseDNs)
     {
-      LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+      LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
       if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
       {
         logger.warn(WARN_SATUACM_ATTR_UNINDEXED, configuration.dn(),
@@ -201,7 +204,8 @@
     Collection<DN> baseDNs = config.getUserBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     // For each base DN, issue an internal search in an attempt to map the
@@ -312,17 +316,17 @@
     // Make sure that the fingerprint attribute is configured for equality in
     // all appropriate backends.
     Set<DN> cfgBaseDNs = configuration.getUserBaseDN();
+    BackendConfigManager backendConfigManager =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
     {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     AttributeType t = configuration.getFingerprintAttribute();
-    BackendConfigManager backendConfigManager =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     for (DN baseDN : cfgBaseDNs)
     {
-      LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+      LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
       if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
       {
         LocalizableMessage message = WARN_SATUACM_ATTR_UNINDEXED.get(
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java
index 4c53420..723fa7a 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java
@@ -60,8 +60,8 @@
   @Override
   public Attribute getValues(Entry entry, VirtualAttributeRule rule)
   {
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(entry.getName());
+    LocalBackend<?> backend = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(entry.getName());
 
     try
     {
@@ -82,8 +82,8 @@
   @Override
   public boolean hasValue(Entry entry, VirtualAttributeRule rule)
   {
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(entry.getName());
+    LocalBackend<?> backend = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(entry.getName());
 
     try
     {
@@ -101,8 +101,8 @@
   @Override
   public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value)
   {
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(entry.getName());
+    LocalBackend<?> backend = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(entry.getName());
     MatchingRule matchingRule =
         rule.getAttributeType().getEqualityMatchingRule();
 
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java
index c6844d2..d71b1e5 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java
@@ -60,8 +60,8 @@
   @Override
   public Attribute getValues(Entry entry, VirtualAttributeRule rule)
   {
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(entry.getName());
+    LocalBackend<?> backend = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(entry.getName());
     try
     {
       long count = backend.getNumberOfChildren(entry.getName());
@@ -81,8 +81,8 @@
   @Override
   public boolean hasValue(Entry entry, VirtualAttributeRule rule)
   {
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(entry.getName());
+    LocalBackend<?> backend = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(entry.getName());
     try
     {
        return backend.getNumberOfChildren(entry.getName()) >= 0;
@@ -97,8 +97,8 @@
   @Override
   public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value)
   {
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(entry.getName());
+    LocalBackend<?> backend = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(entry.getName());
     try
     {
       long count = backend.getNumberOfChildren(entry.getName());
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java
index 17d32c0..97b3fbe 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -59,6 +59,7 @@
 import org.opends.server.api.PasswordStorageScheme;
 import org.opends.server.controls.PasswordPolicyErrorType;
 import org.opends.server.controls.PasswordPolicyResponseControl;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ExtendedOperation;
 import org.opends.server.core.ModifyOperation;
@@ -902,7 +903,9 @@
   {
     try
     {
-      DN matchedDN = DirectoryServer.getParentDNInSuffix(entryDN);
+      BackendConfigManager backendConfigManager =
+          DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+      DN matchedDN = backendConfigManager.getParentDNInSuffix(entryDN);
       while (matchedDN != null)
       {
         if (DirectoryServer.entryExists(matchedDN))
@@ -910,7 +913,7 @@
           return matchedDN;
         }
 
-        matchedDN = DirectoryServer.getParentDNInSuffix(matchedDN);
+        matchedDN = backendConfigManager.getParentDNInSuffix(matchedDN);
       }
     }
     catch (Exception e)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/RegularExpressionIdentityMapper.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/RegularExpressionIdentityMapper.java
index 330f99b..0cc26ec 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/RegularExpressionIdentityMapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/RegularExpressionIdentityMapper.java
@@ -17,6 +17,8 @@
 package org.opends.server.extensions;
 
 import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.util.CollectionUtils.*;
@@ -130,18 +132,18 @@
          currentConfig.getMatchAttribute().toArray(new AttributeType[0]);
 
     Set<DN> cfgBaseDNs = configuration.getMatchBaseDN();
-    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
-    {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
-    }
-
     BackendConfigManager backendConfigManager =
         DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
+    {
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
+    }
+
     for (AttributeType t : attributeTypes)
     {
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           throw new ConfigException(ERR_REGEXMAP_ATTR_UNINDEXED.get(
@@ -199,7 +201,8 @@
     Collection<DN> baseDNs = config.getMatchBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     SearchResultEntry matchingEntry = null;
@@ -287,7 +290,8 @@
     Set<DN> cfgBaseDNs = configuration.getMatchBaseDN();
     if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
     {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      cfgBaseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     BackendConfigManager backendConfigManager =
@@ -296,7 +300,7 @@
     {
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           unacceptableReasons.add(ERR_REGEXMAP_ATTR_UNINDEXED.get(
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SoftReferenceEntryCache.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SoftReferenceEntryCache.java
index 39386c0..e2d34d5 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SoftReferenceEntryCache.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SoftReferenceEntryCache.java
@@ -36,11 +36,11 @@
 import org.forgerock.opendj.config.server.ConfigurationChangeListener;
 import org.forgerock.opendj.server.config.server.EntryCacheCfg;
 import org.forgerock.opendj.server.config.server.SoftReferenceEntryCacheCfg;
-import org.opends.server.api.LocalBackend;
 import org.opends.server.api.DirectoryThread;
 import org.opends.server.api.EntryCache;
 import org.opends.server.api.MonitorData;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ServerContext;
 import org.opends.server.types.CacheEntry;
 import org.forgerock.opendj.ldap.DN;
 import org.opends.server.types.Entry;
@@ -95,7 +95,7 @@
 
   @Override
   public void initializeEntryCache(
-      SoftReferenceEntryCacheCfg configuration
+      ServerContext serverContext, SoftReferenceEntryCacheCfg configuration
       )
       throws ConfigException, InitializationException
   {
@@ -324,22 +324,6 @@
   }
 
   @Override
-  public void clearSubtree(DN baseDN)
-  {
-    // Determine the backend used to hold the specified base DN and clear it.
-    LocalBackend<?> backend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(baseDN);
-    if (backend == null)
-    {
-      // FIXME -- Should we clear everything just to be safe?
-    }
-    else
-    {
-      clearBackend(backend.getBackendID());
-    }
-  }
-
-  @Override
   public void handleLowMemory()
   {
     // This function should automatically be taken care of by the nature of the
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java
index bc16033..a060b54 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java
@@ -18,6 +18,8 @@
 package org.opends.server.extensions;
 
 import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.util.CollectionUtils.*;
@@ -122,7 +124,7 @@
     {
       for (AttributeType t : attributeMap.values())
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           logger.warn(WARN_SATUACM_ATTR_UNINDEXED, configuration.dn(),
@@ -301,7 +303,7 @@
     {
       for (AttributeType t : newAttributeMap.values())
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && !b.isIndexed(t, IndexType.EQUALITY))
         {
           LocalizableMessage message =
@@ -330,7 +332,8 @@
     Set<DN> baseDNs = config.getUserBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
     return baseDNs;
   }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java
index a6fc535..382113d 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java
@@ -47,6 +47,8 @@
 import org.opends.server.types.*;
 
 import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.util.CollectionUtils.*;
@@ -98,17 +100,17 @@
     // Make sure that the subject attribute is configured for equality in all
     // appropriate backends.
     Set<DN> cfgBaseDNs = configuration.getUserBaseDN();
+    BackendConfigManager backendConfigManager =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
     {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     AttributeType t = configuration.getSubjectAttribute();
-    BackendConfigManager backendConfigManager =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     for (DN baseDN : cfgBaseDNs)
     {
-      LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+      LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
       if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
       {
         logger.warn(WARN_SATUACM_ATTR_UNINDEXED, configuration.dn(),
@@ -168,7 +170,8 @@
     Collection<DN> baseDNs = config.getUserBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     // For each base DN, issue an internal search in an attempt to map the
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java
index fa785df..0a7994b 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java
@@ -18,6 +18,8 @@
 package org.opends.server.plugins;
 
 import static org.opends.messages.PluginMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.schema.SchemaConstants.*;
@@ -276,9 +278,11 @@
     }
 
     Set<DN> cfgBaseDNs = pluginCfg.getBaseDN();
+    BackendConfigManager backendConfigManager =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
     {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     // Iterate through all of the defined attribute types and ensure that they
@@ -296,11 +300,9 @@
                              type.getSyntax().getName()));
       }
 
-      BackendConfigManager backendConfigManager =
-          DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && !b.isIndexed(type, IndexType.EQUALITY))
         {
           isAcceptable = false;
@@ -597,7 +599,8 @@
   {
     if (baseDNs.isEmpty())
     {
-      return DirectoryServer.getPublicNamingContexts().keySet();
+      return DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
     return baseDNs;
   }
@@ -1149,7 +1152,8 @@
 
     if (baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     for (DN baseDN : baseDNs)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/SevenBitCleanPlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/SevenBitCleanPlugin.java
index ef2cd7d..0d25bd1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/SevenBitCleanPlugin.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/SevenBitCleanPlugin.java
@@ -17,6 +17,8 @@
 package org.opends.server.plugins;
 
 import static org.opends.messages.PluginMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 
 import java.util.List;
 import java.util.Set;
@@ -346,7 +348,8 @@
     Set<DN> baseDNs = config.getBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
     return isDescendantOfAny(dn, baseDNs);
   }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/UniqueAttributePlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/UniqueAttributePlugin.java
index 1a04a0f..9628eeb 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/UniqueAttributePlugin.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/UniqueAttributePlugin.java
@@ -71,6 +71,8 @@
 import org.opends.server.types.operation.PreOperationModifyOperation;
 
 import static org.opends.messages.PluginMessages.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.opends.server.util.ServerConstants.*;
@@ -150,18 +152,18 @@
     }
 
     Set<DN> cfgBaseDNs = configuration.getBaseDN();
-    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
-    {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
-    }
-
     BackendConfigManager backendConfigManager =
         DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
+    {
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
+    }
+
     for (AttributeType t : configuration.getType())
     {
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           throw new ConfigException(ERR_PLUGIN_UNIQUEATTR_ATTR_UNINDEXED.get(
@@ -550,7 +552,8 @@
     Set<DN> baseDNs = config.getBaseDN();
     if (baseDNs == null || baseDNs.isEmpty())
     {
-      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+      baseDNs = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+          .getNamingContexts(PUBLIC, TOP_LEVEL);
     }
 
     for (DN baseDN : baseDNs)
@@ -684,18 +687,18 @@
     }
 
     Set<DN> cfgBaseDNs = configuration.getBaseDN();
-    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
-    {
-      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
-    }
-
     BackendConfigManager backendConfigManager =
         DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+    if (cfgBaseDNs == null || cfgBaseDNs.isEmpty())
+    {
+      cfgBaseDNs = backendConfigManager.getNamingContexts(PUBLIC, TOP_LEVEL);
+    }
+
     for (AttributeType t : configuration.getType())
     {
       for (DN baseDN : cfgBaseDNs)
       {
-        LocalBackend<?> b = backendConfigManager.getLocalBackend(baseDN);
+        LocalBackend<?> b = backendConfigManager.findLocalBackendForEntry(baseDN);
         if (b != null && ! b.isIndexed(t, IndexType.EQUALITY))
         {
           unacceptableReasons.add(ERR_PLUGIN_UNIQUEATTR_ATTR_UNINDEXED.get(
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/FakeAddOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/FakeAddOperation.java
index bbbbe13..fc9857e 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/FakeAddOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/FakeAddOperation.java
@@ -51,7 +51,8 @@
     return new AddMsg(getCSN(), entry.getName(),
                EntryHistorical.getEntryUUID(entry),
                LDAPReplicationDomain.findEntryUUID(
-                   DirectoryServer.getParentDNInSuffix(entry.getName())),
+                   DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+                   .getParentDNInSuffix(entry.getName())),
                entry.getObjectClasses(),
                entry.getUserAttributes(), entry.getOperationalAttributes());
   }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index 11d176e..0fa85da 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -1668,7 +1668,8 @@
         }
 
         DN entryDN = addOperation.getEntryDN();
-        DN parentDnFromEntryDn = DirectoryServer.getParentDNInSuffix(entryDN);
+        DN parentDnFromEntryDn = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+            .getParentDNInSuffix(entryDN);
         if (parentDnFromEntryDn != null
             && !parentDnFromCtx.equals(parentDnFromEntryDn))
         {
@@ -1959,7 +1960,8 @@
     final CSN csn = generateCSN(addOperation);
     final String entryUUID = getEntryUUID(addOperation);
     final AddContext ctx = new AddContext(csn, entryUUID,
-        findEntryUUID(DirectoryServer.getParentDNInSuffix(addOperation.getEntryDN())));
+        findEntryUUID(DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+            .getParentDNInSuffix(addOperation.getEntryDN())));
     addOperation.setAttachment(SYNCHROCONTEXT, ctx);
   }
 
@@ -3693,7 +3695,8 @@
    */
   private LocalBackend<?> getBackend()
   {
-    return DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(getBaseDN());
+    return DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(getBaseDN());
   }
 
   /*
@@ -3760,7 +3763,7 @@
     }
 
     // Check that the base DN is configured as a base-dn of the directory server
-    if (DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(dn) == null)
+    if (DirectoryServer.getInstance().getServerContext().getBackendConfigManager().findLocalBackendForEntry(dn) == null)
     {
       unacceptableReasons.add(ERR_UNKNOWN_DN.get(dn));
       return false;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java
index 3f10327..a692fa9 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java
@@ -50,6 +50,7 @@
 import org.opends.server.api.ImportTaskListener;
 import org.opends.server.api.RestoreTaskListener;
 import org.opends.server.api.SynchronizationProvider;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ServerContext;
 import org.opends.server.replication.service.DSRSShutdownSync;
@@ -167,10 +168,12 @@
 
     LDAPReplicationDomain domain = null;
     DN temp = dn;
+    BackendConfigManager backendConfigManager =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     while (domain == null && temp != null)
     {
       domain = domains.get(temp);
-      temp = DirectoryServer.getParentDNInSuffix(temp);
+      temp = backendConfigManager.getParentDNInSuffix(temp);
     }
 
     return domain;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tasks/BackupTask.java b/opendj-server-legacy/src/main/java/org/opends/server/tasks/BackupTask.java
index 498cb40..0b4894a 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tasks/BackupTask.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tasks/BackupTask.java
@@ -255,7 +255,7 @@
     {
       for (Map.Entry<String,Entry> mapEntry : configEntries.entrySet())
       {
-        LocalBackend<?> b = getServerContext().getBackendConfigManager().getLocalBackend(mapEntry.getKey());
+        LocalBackend<?> b = getServerContext().getBackendConfigManager().getLocalBackendById(mapEntry.getKey());
         if (b != null && b.supports(BackendOperation.BACKUP))
         {
           backendsToArchive.add(b);
@@ -268,7 +268,7 @@
       // be used.
       for (String id : backendIDList)
       {
-        LocalBackend<?> b = getServerContext().getBackendConfigManager().getLocalBackend(id);
+        LocalBackend<?> b = getServerContext().getBackendConfigManager().getLocalBackendById(id);
         if (b == null || configEntries.get(id) == null)
         {
           logger.error(ERR_BACKUPDB_NO_BACKENDS_FOR_ID, id);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tasks/ExportTask.java b/opendj-server-legacy/src/main/java/org/opends/server/tasks/ExportTask.java
index 794c5f4..3ef7735 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tasks/ExportTask.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tasks/ExportTask.java
@@ -261,7 +261,7 @@
 
     // Get the backend into which the LDIF should be imported.
 
-    LocalBackend<?> backend = getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+    LocalBackend<?> backend = getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
 
     if (backend == null)
     {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java b/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java
index 37fccc6..222ce30 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java
@@ -39,6 +39,7 @@
 import org.opends.messages.TaskMessages;
 import org.opends.server.api.LocalBackend;
 import org.opends.server.api.LocalBackend.BackendOperation;
+import org.opends.server.api.Backend;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.backends.task.Task;
 import org.opends.server.backends.task.TaskState;
@@ -283,7 +284,7 @@
 
     if(backendID != null)
     {
-      backend = getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+      backend = getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
       if (backend == null)
       {
         LocalizableMessage message = ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID.get();
@@ -301,7 +302,7 @@
       BackendConfigManager backendConfigManager = getServerContext().getBackendConfigManager();
       for(DN includeBranch : includeBranches)
       {
-        LocalBackend<?> locatedBackend = backendConfigManager.getLocalBackend(includeBranch);
+        LocalBackend<?> locatedBackend = backendConfigManager.findLocalBackendForEntry(includeBranch);
         if(locatedBackend != null)
         {
           if(backend == null)
@@ -441,7 +442,7 @@
 
     if(backendID != null)
     {
-      backend = getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+      backend = getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
 
       if (backend == null)
       {
@@ -460,7 +461,7 @@
       BackendConfigManager backendConfigManager = getServerContext().getBackendConfigManager();
       for(DN includeBranch : includeBranches)
       {
-        LocalBackend<?> locatedBackend = backendConfigManager.getLocalBackend(includeBranch);
+        LocalBackend<?> locatedBackend = backendConfigManager.findLocalBackendForEntry(includeBranch);
         if(locatedBackend != null)
         {
           if(backend == null)
@@ -479,23 +480,9 @@
 
     // Find backends with subordinate base DNs that should be excluded from the import.
     defaultIncludeBranches = new HashSet<>(backend.getBaseDNs());
-
-    if (backend.getSubordinateBackends() != null)
+    for (Backend<?> subBackend : getServerContext().getBackendConfigManager().getSubordinateBackends(backend))
     {
-      for (LocalBackend<?> subBackend : backend.getSubordinateBackends())
-      {
-        for (DN baseDN : subBackend.getBaseDNs())
-        {
-          for (DN importBase : defaultIncludeBranches)
-          {
-            if (!baseDN.equals(importBase) && baseDN.isSubordinateOrEqualTo(importBase))
-            {
-              excludeBranches.add(baseDN);
-              break;
-            }
-          }
-        }
-      }
+      excludeBranches.addAll(subBackend.getBaseDNs());
     }
 
     for (String s : excludeBranchStrings)
@@ -717,7 +704,7 @@
         // It is necessary to retrieve the backend structure again
         // because disabling and enabling it again may have resulted
         // in a new backend being registered to the server.
-        backend = getServerContext().getBackendConfigManager().getLocalBackend(backend.getBackendID());
+        backend = getServerContext().getBackendConfigManager().getLocalBackendById(backend.getBackendID());
       }
       catch (DirectoryException e)
       {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java b/opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java
index faf6017..4ffcb73 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java
@@ -261,7 +261,7 @@
 
     String backendID = TaskUtils.getBackendID(configEntry);
 
-    LocalBackend<?> backend = getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+    LocalBackend<?> backend = getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
     if (!backend.supports(BackendOperation.RESTORE))
     {
       logger.error(ERR_RESTOREDB_CANNOT_RESTORE, backend.getBackendID());
@@ -341,7 +341,7 @@
           // it is necessary to retrieve the backend structure again
           // because disabling and enabling it again may have resulted
           // in a new backend being registered to the server.
-          backend = getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+          backend = getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
         } catch (DirectoryException e)
         {
           logger.traceException(e);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java b/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
index 85be42b..ccd802c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
@@ -2052,7 +2052,8 @@
       else
       {
         // Get the DN of the parent entry if possible.
-        DN parentDN = DirectoryServer.getParentDNInSuffix(dn);
+        DN parentDN = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+            .getParentDNInSuffix(dn);
         if (parentDN != null)
         {
           try
@@ -2123,7 +2124,8 @@
       }
       else if (! parentProvided)
       {
-        DN parentDN = DirectoryServer.getParentDNInSuffix(getName());
+        DN parentDN = DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
+            .getParentDNInSuffix(getName());
         if (parentDN != null)
         {
           try
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index 9bc806b..680a768 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -54,6 +54,7 @@
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.AddOperationWrapper;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.PasswordPolicy;
 import org.opends.server.core.PersistentSearch;
@@ -230,8 +231,10 @@
         return;
       }
 
-      DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
-      if (parentDN == null && !DirectoryServer.isNamingContext(entryDN))
+      BackendConfigManager backendConfigManager =
+          DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+      DN parentDN = backendConfigManager.getParentDNInSuffix(entryDN);
+      if (parentDN == null && !backendConfigManager.containsLocalNamingContext(entryDN))
       {
         if (entryDN.isRootDN())
         {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
index 0104749..aa59cda 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -153,7 +153,7 @@
     // If the target entry is in the server configuration, then make sure the
     // requester has the CONFIG_READ privilege.
     if (DirectoryServer.getInstance().getServerContext().getBackendConfigManager()
-          .getLocalBackend(ConfigurationBackend.CONFIG_BACKEND_ID).handlesEntry(entryDN)
+          .getLocalBackendById(ConfigurationBackend.CONFIG_BACKEND_ID).handlesEntry(entryDN)
         && !clientConnection.hasPrivilege(Privilege.CONFIG_READ, this))
     {
       appendErrorMessage(ERR_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES.get());
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
index 185f053..179aa50 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -28,6 +28,7 @@
 import org.opends.server.controls.LDAPAssertionRequestControl;
 import org.opends.server.controls.LDAPPreReadRequestControl;
 import org.opends.server.core.AccessControlConfigManager;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DeleteOperation;
 import org.opends.server.core.DeleteOperationWrapper;
 import org.opends.server.core.DirectoryServer;
@@ -273,18 +274,13 @@
       // the entry actually exists and does not have any children (or possibly
       // handling a subtree delete). But we will need to check if there are
       // any subordinate backends that should stop us from attempting the delete
-      for (LocalBackend<?> b : backend.getSubordinateBackends())
+      BackendConfigManager backendConfigManager =
+          DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+      for (DN dn : backendConfigManager.findSubordinateLocalNamingContextsForEntry(entryDN))
       {
-        for (DN dn : b.getBaseDNs())
-        {
-          if (dn.isSubordinateOrEqualTo(entryDN))
-          {
-            setResultCodeAndMessageNoInfoDisclosure(entry,
-                ResultCode.NOT_ALLOWED_ON_NONLEAF,
-                ERR_DELETE_HAS_SUB_BACKEND.get(entryDN, dn));
-            return;
-          }
-        }
+        setResultCodeAndMessageNoInfoDisclosure(entry,
+            ResultCode.NOT_ALLOWED_ON_NONLEAF, ERR_DELETE_HAS_SUB_BACKEND.get(entryDN, dn));
+        return;
       }
 
       // Actually perform the delete.
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
index 1f2408a..a8fc30b 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -37,6 +37,7 @@
 import org.opends.server.controls.LDAPPostReadRequestControl;
 import org.opends.server.controls.LDAPPreReadRequestControl;
 import org.opends.server.core.AccessControlConfigManager;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyDNOperationWrapper;
@@ -235,10 +236,12 @@
     }
 
     // Construct the new DN to use for the entry.
+    BackendConfigManager backendConfigManager =
+        DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
     DN parentDN;
     if (newSuperior == null)
     {
-      parentDN = DirectoryServer.getParentDNInSuffix(entryDN);
+      parentDN = backendConfigManager.getParentDNInSuffix(entryDN);
     }
     else
     {
@@ -270,8 +273,7 @@
       return;
     }
 
-    LocalBackend<?> newBackend =
-        DirectoryServer.getInstance().getServerContext().getBackendConfigManager().getLocalBackend(newDN);
+    LocalBackend<?> newBackend = backendConfigManager.findLocalBackendForEntry(newDN);
     if (newBackend == null)
     {
       setResultCode(ResultCode.NO_SUCH_OBJECT);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 8cd729d..7763541 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -37,7 +37,6 @@
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.BackendConfigManager;
-import org.opends.server.core.BackendConfigManager.BackendAndName;
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.CompareOperation;
 import org.opends.server.core.DeleteOperation;
@@ -728,9 +727,9 @@
    */
   public static boolean execute(Operation operation, DN entryDN) throws CanceledOperationException
   {
-    BackendAndName backendAndName = getBackendManager().getLocalBackendAndName(entryDN);
+    LocalBackend<?> backend = getBackendManager().findLocalBackendForEntry(entryDN);
 
-    if (backendAndName == null)
+    if (backend == null)
     {
       // We have found no backend for the requested base DN,
       // just return a no such entry result code and stop the processing.
@@ -741,12 +740,12 @@
       return false;
     }
 
-    executeOperation(operation, backendAndName.getBackend());
+    executeOperation(operation, backend);
 
     // For subtree search operation we need to go through the subordinate nodes.
     if (operation.getOperationType() == OperationType.SEARCH)
     {
-      executeSearchOnSubordinates((SearchOperation) operation, backendAndName.getBaseDn());
+      executeSearchOnSubordinates((SearchOperation) operation);
     }
     return true;
   }
@@ -766,7 +765,7 @@
    * @throws CanceledOperationException
    *           if this operation should be canceled.
    */
-  private static void executeSearchOnSubordinates(SearchOperation searchOp, DN baseDN)
+  private static void executeSearchOnSubordinates(SearchOperation searchOp)
       throws CanceledOperationException {
     SearchScope originalScope = searchOp.getScope();
     if (originalScope == SearchScope.BASE_OBJECT)
@@ -779,13 +778,12 @@
 
     SearchResultCode searchResultCode = new SearchResultCode(searchOp.getResultCode(), searchOp.getErrorMessage());
     DN originalBaseDN = searchOp.getBaseDN();
-    for (BackendAndName subordinate : getBackendManager().getSubordinateBackends(baseDN))
+    for (DN subordinateDN : getBackendManager().findSubordinateLocalNamingContextsForEntry(originalBaseDN))
     {
       // We have to change the operation request base DN to match the
       // subordinate workflow base DN. Otherwise the workflow will
       // return a no such entry result code as the operation request
       // base DN is a superior of the subordinate workflow base DN.
-      DN subordinateDN = subordinate.getBaseDn();
 
       // If the new search scope is 'base' and the search base DN does not
       // map the subordinate workflow then skip the subordinate workflow.
@@ -853,7 +851,8 @@
   {
     try
     {
-      DN matchedDN = DirectoryServer.getParentDNInSuffix(entryDN);
+      BackendConfigManager backendManager = getBackendManager();
+      DN matchedDN = backendManager.getParentDNInSuffix(entryDN);
       while (matchedDN != null)
       {
         if (DirectoryServer.entryExists(matchedDN))
@@ -861,7 +860,7 @@
           return matchedDN;
         }
 
-        matchedDN = DirectoryServer.getParentDNInSuffix(matchedDN);
+        matchedDN = backendManager.getParentDNInSuffix(matchedDN);
       }
     }
     catch (Exception e)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
index c028803..653c67d 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
@@ -891,7 +891,7 @@
     // to memory backend must be invalidated. So to prevent this problem, we
     // retrieve the memory backend reference each time before cleaning it.
     BackendConfigManager backendConfigManager = getServerContext().getBackendConfigManager();
-    MemoryBackend memoryBackend = (MemoryBackend) backendConfigManager.getLocalBackend(backendID);
+    MemoryBackend memoryBackend = (MemoryBackend) backendConfigManager.getLocalBackendById(backendID);
 
     if (memoryBackend == null)
     {
@@ -916,7 +916,7 @@
   public static void clearMemoryBackend(String backendID) throws Exception
   {
     MemoryBackend memoryBackend =
-        (MemoryBackend) getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+        (MemoryBackend) getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
     // FIXME JNR I suspect we could call finalizeBackend() here (but also in other
     // places in this class), because finalizeBackend() calls clearMemoryBackend().
     if (memoryBackend != null)
@@ -944,7 +944,7 @@
    */
   public static void clearBackend(String backendId, String baseDN) throws Exception
   {
-    LocalBackend<?> b = getServerContext().getBackendConfigManager().getLocalBackend(backendId);
+    LocalBackend<?> b = getServerContext().getBackendConfigManager().getLocalBackendById(backendId);
     if (clearBackend(b) && baseDN != null)
     {
       Entry e = createEntry(DN.valueOf(baseDN));
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java
index b785bc8..0715366 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java
@@ -1530,7 +1530,7 @@
     //  retrieve the memory backend reference each time before cleaning it.
     BackendConfigManager backendConfigManager = TestCaseUtils.getServerContext().getBackendConfigManager();
     MemoryBackend memoryBackend =
-        (MemoryBackend) backendConfigManager.getLocalBackend(backendId);
+        (MemoryBackend) backendConfigManager.getLocalBackendById(backendId);
 
     if (memoryBackend == null)
     {
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/GenericBackendTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/GenericLocalBackendTestCase.java
similarity index 90%
rename from opendj-server-legacy/src/test/java/org/opends/server/backends/GenericBackendTestCase.java
rename to opendj-server-legacy/src/test/java/org/opends/server/backends/GenericLocalBackendTestCase.java
index fdcd92f..02bd642 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/GenericBackendTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/GenericLocalBackendTestCase.java
@@ -33,7 +33,7 @@
 
 /** A set of generic test cases that apply to all Directory Server backends. */
 @SuppressWarnings("javadoc")
-public class GenericBackendTestCase extends BackendTestCase
+public class GenericLocalBackendTestCase extends BackendTestCase
 {
   /** Ensures that the Directory Server is running. */
   @BeforeClass
@@ -49,7 +49,7 @@
    * @return  The backends defined in the server.
    */
   @DataProvider(name = "backends")
-  public Object[][] getBackends()
+  public Object[][] getLocalBackends()
   {
     List<LocalBackend<?>> backendList = new ArrayList<>(
         TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackends());
@@ -156,20 +156,6 @@
     b.getEntryCount();
   }
 
-  /** Tests the {@link LocalBackend#getParentBackend} method for the provided backend. */
-  @Test(dataProvider = "backends")
-  public void testGetParentBackend(LocalBackend<?> b)
-  {
-    b.getParentBackend();
-  }
-
-  /** Tests the {@link LocalBackend#getSubordinateBackends} method for the provided backend. */
-  @Test(dataProvider = "backends")
-  public void testGetSubordinateBackends(LocalBackend<?> b)
-  {
-    assertNotNull(b.getSubordinateBackends());
-  }
-
   /**
    * Tests the {@link LocalBackend#handlesEntry} method for the provided backend for each of the declared
    * base DNs.
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/LDIFBackendTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/LDIFBackendTestCase.java
index 4eaa3a4..77415e0 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/LDIFBackendTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/LDIFBackendTestCase.java
@@ -726,7 +726,7 @@
 
   private LDIFBackend getLDIFBackend()
   {
-    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend("ldifRoot");
+    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById("ldifRoot");
     assertNotNull(b);
     assertTrue(b instanceof LDIFBackend);
     return (LDIFBackend) b;
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java
index 4ed0698..28a0e3d 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java
@@ -91,7 +91,7 @@
     TestCaseUtils.startServer();
 
     schemaBackend =
-        (SchemaBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend("schema");
+        (SchemaBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById("schema");
     assertNotNull(schemaBackend);
   }
 
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/task/TaskBackendTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/task/TaskBackendTestCase.java
index da5dc9c..b1fdee1 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/task/TaskBackendTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/task/TaskBackendTestCase.java
@@ -471,8 +471,8 @@
             + ",cn=Recurring Tasks,cn=tasks";
     String taskSchedule = "00 * * " + scheduledMonth + " *";
 
-    TaskBackend taskBackend =
-      (TaskBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("cn=tasks"));
+    TaskBackend taskBackend = (TaskBackend) TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("cn=tasks"));
     long tasksCountBefore = taskBackend.getNumberOfEntriesInBaseDN(DN.valueOf("cn=Scheduled Tasks,cn=tasks"));
 
     assertTrue(addRecurringTask(taskID, taskSchedule));
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/core/AddOperationTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/core/AddOperationTestCase.java
index 7aed2f1..9b16501 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/core/AddOperationTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/core/AddOperationTestCase.java
@@ -73,7 +73,7 @@
   @AfterMethod(alwaysRun=true)
   public void reenableBackend() throws DirectoryException {
     LocalBackend<?> b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     b.setWritabilityMode(WritabilityMode.ENABLED);
   }
 
@@ -1211,7 +1211,7 @@
          "userPassword: password");
 
     LocalBackend<?> b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     b.setWritabilityMode(WritabilityMode.DISABLED);
 
     AddOperation addOperation = getRootConnection().processAdd(entry);
@@ -1244,7 +1244,7 @@
          "userPassword: password");
 
     LocalBackend<?> b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     b.setWritabilityMode(WritabilityMode.INTERNAL_ONLY);
 
     AddOperation addOperation = getRootConnection().processAdd(entry);
@@ -1270,7 +1270,7 @@
       conn.bind("cn=Directory Manager", "password");
 
       LocalBackend<?> b =
-          TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+          TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
       b.setWritabilityMode(WritabilityMode.INTERNAL_ONLY);
 
       long addRequests  = ldapStatistics.getAddRequests();
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/core/BackendConfigManagerTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/core/BackendConfigManagerTestCase.java
index ef40508..52dab6b 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/core/BackendConfigManagerTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/core/BackendConfigManagerTestCase.java
@@ -17,35 +17,58 @@
 package org.opends.server.core;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.ConditionResult;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.forgerock.opendj.server.config.server.BackendCfg;
+import org.forgerock.opendj.server.config.server.LocalBackendCfg;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.api.Backend;
 import org.opends.server.api.LocalBackend;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.internal.SearchRequest;
+import org.opends.server.types.BackupConfig;
+import org.opends.server.types.BackupDirectory;
+import org.opends.server.types.CanceledOperationException;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
+import org.opends.server.types.IndexType;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.LDIFExportConfig;
+import org.opends.server.types.LDIFImportConfig;
+import org.opends.server.types.LDIFImportResult;
+import org.opends.server.types.RestoreConfig;
 import org.opends.server.util.StaticUtils;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import static org.mockito.Mockito.*;
 import static org.forgerock.opendj.ldap.ModificationType.*;
 import static org.forgerock.opendj.ldap.requests.Requests.*;
 import static org.opends.server.TestCaseUtils.*;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.LOCAL;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PRIVATE;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.opends.server.protocols.internal.InternalClientConnection.*;
 import static org.opends.server.protocols.internal.Requests.*;
 import static org.testng.Assert.*;
+import static org.assertj.core.api.Assertions.*;
 
 /**
  * A set of generic test cases that cover adding, modifying, and removing
  * Directory Server backends.
  */
-public class BackendConfigManagerTestCase
-       extends CoreTestCase
+public class BackendConfigManagerTestCase extends CoreTestCase
 {
   /**
    * Ensures that the Directory Server is running.
@@ -58,6 +81,208 @@
     TestCaseUtils.startServer();
   }
 
+  /** Does not require the server to be started .*/
+  @Test
+  public void testBackendHierarchy() throws Exception
+  {
+    ServerContext serverContext = mock(ServerContext.class);
+    BackendConfigManager manager = new BackendConfigManager(serverContext);
+
+    // define 3 local backends, including one private, and one non-local backend
+    LocalBackend<? extends LocalBackendCfg> backend1 = mockLocalBackend("user1", "o=parent");
+    Backend<? extends BackendCfg> backend2 = mockBackend("user2", "ou=child,o=parent");
+    LocalBackend<? extends LocalBackendCfg> backend3 = mockLocalBackend("user3", "ou=grandchild,ou=child,o=parent");
+    LocalBackend<? extends LocalBackendCfg> backend4 = mockLocalBackend("private", "o=private");
+
+    manager.registerBaseDN(dn("o=parent"), backend1, false);
+    manager.registerBaseDN(dn("ou=child,o=parent"), backend2, false);
+    manager.registerBaseDN(dn("ou=grandchild,ou=child,o=parent"), backend3, false);
+    manager.registerBaseDN(dn("o=private"), backend4, true);
+
+    assertThat(manager.getAllBackends()).containsOnly(backend1, backend2, backend3, backend4);
+    assertThat(manager.getLocalBackends()).containsOnly(backend1, backend3, backend4);
+    assertThat(manager.getNamingContexts(PUBLIC)).containsOnly(
+        dn("o=parent"), dn("ou=child,o=parent"), dn("ou=grandchild,ou=child,o=parent"));
+    assertThat(manager.getNamingContexts(PUBLIC, TOP_LEVEL)).containsOnly(dn("o=parent"));
+    assertThat(manager.getNamingContexts(PRIVATE)).containsOnly(dn("o=private"));
+    assertThat(manager.getNamingContexts(PUBLIC, LOCAL)).containsOnly(
+        dn("o=parent"), dn("ou=grandchild,ou=child,o=parent"));
+    assertThat(manager.getNamingContexts(PUBLIC, LOCAL, TOP_LEVEL)).containsOnly(dn("o=parent"));
+    assertThat(manager.containsLocalNamingContext(dn("o=parent"))).isTrue();
+    assertThat(manager.containsLocalNamingContext(dn("ou=child,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("ou=grandchild,ou=child,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("o=private"))).isTrue();
+
+    assertThat(manager.getSubordinateBackends(backend1)).containsOnly(backend2);
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("o=parent"))).isEmpty();
+
+    assertThat(manager.getSubordinateBackends(backend2)).containsOnly(backend3);
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("ou=child,o=parent")))
+      .containsOnly(dn("ou=grandchild,ou=child,o=parent"));
+
+    assertThat(manager.getSubordinateBackends(backend3)).isEmpty();
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("ou=grandchild,ou=child,o=parent"))).isEmpty();
+
+    assertThat(manager.getSubordinateBackends(backend4)).isEmpty();
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("o=private"))).isEmpty();
+
+    assertThat(manager.findBackendForEntry(dn("o=anotherparent"))).isNull();
+    assertThat(manager.findBackendForEntry(dn("o=parent"))).isEqualTo(backend1);
+    assertThat(manager.findNamingContextForEntry(dn("o=parent")).toString()).isEqualTo("o=parent");
+    assertThat(manager.findLocalBackendForEntry(dn("o=parent"))).isEqualTo(backend1);
+
+    assertThat(manager.findBackendForEntry(dn("ou=anotherchild,o=parent"))).isEqualTo(backend1);
+    assertThat(manager.findNamingContextForEntry(dn("ou=anotherchild,o=parent")).toString()).isEqualTo("o=parent");
+    assertThat(manager.findLocalBackendForEntry(dn("ou=anotherchild,o=parent"))).isEqualTo(backend1);
+
+    assertThat(manager.findBackendForEntry(dn("ou=child,o=parent"))).isEqualTo(backend2);
+    assertThat(manager.findNamingContextForEntry(dn("ou=child,o=parent")).toString()).isEqualTo("ou=child,o=parent");
+    assertThat(manager.findLocalBackendForEntry(dn("ou=child,o=parent"))).isNull();
+    assertThat(manager.findBackendForEntry(dn("ou=anothergrandchild,ou=child,o=parent"))).isEqualTo(backend2);
+    assertThat(manager.findNamingContextForEntry(dn("ou=anothergrandchild,ou=child,o=parent")).toString())
+      .isEqualTo("ou=child,o=parent");
+
+    assertThat(manager.findBackendForEntry(dn("ou=grandchild,ou=child,o=parent"))).isEqualTo(backend3);
+    assertThat(manager.findNamingContextForEntry(dn("ou=grandchild,ou=child,o=parent")).toString())
+      .isEqualTo("ou=grandchild,ou=child,o=parent");
+    assertThat(manager.findLocalBackendForEntry(dn("ou=grandchild,ou=child,o=parent"))).isEqualTo(backend3);
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=grandchild,ou=child,o=parent"))).isEqualTo(backend3);
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=grandchild,ou=child,o=parent")).toString())
+      .isEqualTo("ou=grandchild,ou=child,o=parent");
+
+    assertThat(manager.findBackendForEntry(dn("o=private"))).isEqualTo(backend4);
+    assertThat(manager.findNamingContextForEntry(dn("ou=privchild,o=private")).toString()).isEqualTo("o=private");
+    assertThat(manager.findBackendForEntry(dn("ou=privchild,o=private"))).isEqualTo(backend4);
+
+  }
+
+  /** Does not require the server to be started .*/
+  @Test
+  public void testBackendHierarchyMultiple() throws Exception
+  {
+    ServerContext serverContext = mock(ServerContext.class);
+    BackendConfigManager manager = new BackendConfigManager(serverContext);
+
+    // define 4 local backends and one non-local backend at the bottom of the hierarchy
+    LocalBackend<? extends LocalBackendCfg> backend1 = mockLocalBackend("user1", "o=parent", "o=parent2");
+    LocalBackend<? extends LocalBackendCfg> backend2 =
+        mockLocalBackend("user2", "ou=child21,o=parent", "ou=child22,o=parent");
+    LocalBackend<? extends LocalBackendCfg> backend3 =
+        mockLocalBackend("user3", "ou=child31,o=parent", "ou=child32,o=parent");
+    LocalBackend<? extends LocalBackendCfg> backend4 = mockLocalBackend("user4", "ou=grandchild,ou=child21,o=parent");
+    Backend<? extends BackendCfg> backend5 = mockBackend("user5", "ou=grandchild,ou=child31,o=parent");
+
+    manager.registerBaseDN(dn("o=parent"), backend1, false);
+    manager.registerBaseDN(dn("o=parent2"), backend1, false);
+    manager.registerBaseDN(dn("ou=child21,o=parent"), backend2, false);
+    manager.registerBaseDN(dn("ou=child22,o=parent"), backend2, false);
+    manager.registerBaseDN(dn("ou=child31,o=parent"), backend3, false);
+    manager.registerBaseDN(dn("ou=child32,o=parent"), backend3, false);
+    manager.registerBaseDN(dn("ou=grandchild,ou=child21,o=parent"), backend4, false);
+    manager.registerBaseDN(dn("ou=grandchild,ou=child31,o=parent"), backend5, false);
+
+    assertThat(manager.getAllBackends()).containsOnly(backend1, backend2, backend3, backend4, backend5);
+    assertThat(manager.getLocalBackends()).containsOnly(backend1, backend2, backend3, backend4);
+    assertThat(manager.getNamingContexts(PUBLIC)).containsOnly(
+        dn("o=parent"), dn("o=parent2"), dn("ou=child21,o=parent"), dn("ou=child22,o=parent"),
+        dn("ou=child31,o=parent"), dn("ou=child32,o=parent"), dn("ou=grandchild,ou=child21,o=parent"),
+        dn("ou=grandchild,ou=child31,o=parent"));
+    assertThat(manager.getNamingContexts(PUBLIC, TOP_LEVEL))
+      .containsOnly(dn("o=parent"), dn("o=parent2"));
+    assertThat(manager.getNamingContexts(PRIVATE)).isEmpty();
+    assertThat(manager.getNamingContexts(PUBLIC, LOCAL)).containsOnly(
+        dn("o=parent"), dn("o=parent2"), dn("ou=child21,o=parent"), dn("ou=child22,o=parent"),
+        dn("ou=child31,o=parent"), dn("ou=child32,o=parent"), dn("ou=grandchild,ou=child21,o=parent"));
+    assertThat(manager.getNamingContexts(PUBLIC, LOCAL, TOP_LEVEL))
+      .containsOnly(dn("o=parent"), dn("o=parent2"));
+    assertThat(manager.containsLocalNamingContext(dn("o=parent"))).isTrue();
+    assertThat(manager.containsLocalNamingContext(dn("o=parent2"))).isTrue();
+    assertThat(manager.containsLocalNamingContext(dn("ou=child21,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("ou=child22,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("ou=child31,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("ou=child32,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("ou=grandchild,ou=child21,o=parent"))).isFalse();
+    assertThat(manager.containsLocalNamingContext(dn("ou=grandchild,ou=child31,o=parent"))).isFalse();
+
+    assertThat(manager.getSubordinateBackends(backend1)).containsOnly(backend2, backend3);
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("o=parent"))).containsOnly(
+        dn("ou=child21,o=parent"), dn("ou=child22,o=parent"),
+        dn("ou=child31,o=parent"), dn("ou=child32,o=parent"));
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("o=parent"))).containsOnly(
+        dn("ou=child21,o=parent"), dn("ou=child22,o=parent"),
+        dn("ou=child31,o=parent"), dn("ou=child32,o=parent"));
+
+    assertThat(manager.getSubordinateBackends(backend2)).containsOnly(backend4);
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("ou=child21,o=parent")))
+      .containsExactly(dn("ou=grandchild,ou=child21,o=parent"));
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("ou=child22,o=parent"))).isEmpty();
+
+    assertThat(manager.getSubordinateBackends(backend3)).containsExactly(backend5);
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("ou=child31,o=parent"))).isEmpty();
+    assertThat(manager.findSubordinateLocalNamingContextsForEntry(dn("ou=child32,o=parent"))).isEmpty();
+
+    assertThat(manager.getSubordinateBackends(backend4)).isEmpty();
+    assertThat(manager.getSubordinateBackends(backend5)).isEmpty();
+
+    assertThat(manager.findBackendForEntry(dn("o=parent"))).isEqualTo(backend1);
+    assertThat(manager.findNamingContextForEntry(dn("o=parent")).toString()).isEqualTo("o=parent");
+    assertThat(manager.findBackendForEntry(dn("o=parent2"))).isEqualTo(backend1);
+    assertThat(manager.findNamingContextForEntry(dn("o=parent2")).toString()).isEqualTo("o=parent2");
+
+    assertThat(manager.findBackendForEntry(dn("ou=child21,o=parent"))).isEqualTo(backend2);
+    assertThat(manager.findNamingContextForEntry(dn("ou=child21,o=parent")).toString())
+      .isEqualTo("ou=child21,o=parent");
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=child21,o=parent"))).isEqualTo(backend2);
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=child21,o=parent")).toString())
+      .isEqualTo("ou=child21,o=parent");
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=child22,o=parent"))).isEqualTo(backend2);
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=child22,o=parent")).toString())
+      .isEqualTo("ou=child22,o=parent");
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=child31,o=parent"))).isEqualTo(backend3);
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=child31,o=parent")).toString())
+      .isEqualTo("ou=child31,o=parent");
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=child32,o=parent"))).isEqualTo(backend3);
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=child32,o=parent")).toString())
+      .isEqualTo("ou=child32,o=parent");
+
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=grandchild,ou=child21,o=parent"))).isEqualTo(backend4);
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=grandchild,ou=child21,o=parent")).toString())
+      .isEqualTo("ou=grandchild,ou=child21,o=parent");
+    assertThat(manager.findLocalBackendForEntry(dn("ou=another,ou=grandchild,ou=child21,o=parent")))
+      .isEqualTo(backend4);
+    assertThat(manager.findBackendForEntry(dn("ou=another,ou=grandchild,ou=child31,o=parent")))
+      .isEqualTo(backend5);
+    assertThat(manager.findLocalBackendForEntry(dn("ou=another,ou=grandchild,ou=child31,o=parent")))
+      .isNull();
+    assertThat(manager.findNamingContextForEntry(dn("ou=another,ou=grandchild,ou=child31,o=parent")).toString())
+      .isEqualTo("ou=grandchild,ou=child31,o=parent");
+  }
+
+  private DN dn(String dn)
+  {
+    return DN.valueOf(dn);
+  }
+
+  private Set<DN> toDNs(String... dns)
+  {
+    HashSet<DN> set = new HashSet<DN>();
+    for (String dn : dns)
+    {
+      set.add(dn(dn));
+    }
+    return set;
+  }
+
+  private Backend<? extends BackendCfg> mockBackend(final String backendId, final String... baseDNs)
+  {
+    return new BackendMock(backendId, toDNs(baseDNs));
+  }
+
+  private LocalBackend<? extends LocalBackendCfg> mockLocalBackend(String backendId, String... baseDNs)
+  {
+    return new LocalBackendMock(backendId, toDNs(baseDNs));
+  }
+
   /**
    * Tests that the server will reject an attempt to register a base DN that is
    * already defined in the server.
@@ -129,7 +354,7 @@
     Entry backendEntry = createBackendEntry(backendID, false, baseDN);
 
     processAdd(backendEntry);
-    assertNull(getBackendConfigManager().getLocalBackend(backendID));
+    assertNull(getBackendConfigManager().getLocalBackendById(backendID));
     assertNull(getBackendConfigManager().getLocalBackendWithBaseDN(baseDN));
 
     DeleteOperation deleteOperation = getRootConnection().processDelete(backendEntry.getName());
@@ -153,13 +378,13 @@
 
     processAdd(backendEntry);
 
-    LocalBackend<?> backend = getBackendConfigManager().getLocalBackend(backendID);
+    LocalBackend<?> backend = getBackendConfigManager().getLocalBackendById(backendID);
     assertBackend(baseDN, backend);
     createEntry(baseDN, backend);
 
     DeleteOperation deleteOperation = getRootConnection().processDelete(backendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(backendID));
+    assertNull(getBackendConfigManager().getLocalBackendById(backendID));
   }
 
 
@@ -181,7 +406,7 @@
 
     processAdd(backendEntry);
     assertNull(getLocalBackend(backendID));
-    assertFalse(DirectoryServer.isNamingContext(baseDN));
+    assertFalse(getServerContext().getBackendConfigManager().containsLocalNamingContext(baseDN));
 
     // Modify the backend to enable it.
     enableBackend(backendEntry, true);
@@ -194,7 +419,7 @@
     enableBackend(backendEntry, false);
     assertNull(getLocalBackend(backendID));
     assertFalse(DirectoryServer.entryExists(baseDN));
-    assertFalse(DirectoryServer.isNamingContext(baseDN));
+    assertFalse(getServerContext().getBackendConfigManager().containsLocalNamingContext(baseDN));
 
 
     // Delete the disabled backend.
@@ -204,7 +429,7 @@
 
   private LocalBackend<?> getLocalBackend(String backendID)
   {
-    return getServerContext().getBackendConfigManager().getLocalBackend(backendID);
+    return getServerContext().getBackendConfigManager().getLocalBackendById(backendID);
   }
 
 
@@ -225,7 +450,7 @@
                                                   parentBaseDN);
     processAdd(parentBackendEntry);
 
-    LocalBackend<?> parentBackend = getBackendConfigManager().getLocalBackend(parentBackendID);
+    LocalBackend<?> parentBackend = getBackendConfigManager().getLocalBackendById(parentBackendID);
     assertBackend(parentBaseDN, parentBackend);
     createEntry(parentBaseDN, parentBackend);
 
@@ -237,14 +462,12 @@
                                                  childBaseDN);
     processAdd(childBackendEntry);
 
-    LocalBackend<?> childBackend = getBackendConfigManager().getLocalBackend(childBackendID);
+    LocalBackend<?> childBackend = getBackendConfigManager().getLocalBackendById(childBackendID);
     assertNotNull(childBackend);
     assertEquals(childBackend, getBackendConfigManager().getLocalBackendWithBaseDN(childBaseDN));
-    assertNotNull(childBackend.getParentBackend());
-    assertEquals(parentBackend, childBackend.getParentBackend());
-    assertEquals(parentBackend.getSubordinateBackends().length, 1);
+    assertThat(getBackendConfigManager().getSubordinateBackends(parentBackend)).containsExactly(childBackend);
     assertFalse(childBackend.entryExists(childBaseDN));
-    assertFalse(DirectoryServer.isNamingContext(childBaseDN));
+    assertFalse(getBackendConfigManager().containsLocalNamingContext(childBaseDN));
 
     createEntry(childBaseDN, childBackend);
 
@@ -261,17 +484,17 @@
     // Make sure that we can't remove the parent backend with the child still in place.
     DeleteOperation deleteOperation = conn.processDelete(parentBackendEntry.getName());
     assertNotEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNotNull(getBackendConfigManager().getLocalBackend(parentBackendID));
+    assertNotNull(getBackendConfigManager().getLocalBackendById(parentBackendID));
 
     // Delete the child and then delete the parent.
     deleteOperation = conn.processDelete(childBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(childBackendID));
-    assertEquals(parentBackend.getSubordinateBackends().length, 0);
+    assertNull(getBackendConfigManager().getLocalBackendById(childBackendID));
+    assertThat(getBackendConfigManager().getSubordinateBackends(parentBackend)).isEmpty();
 
     deleteOperation = conn.processDelete(parentBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(parentBackendID));
+    assertNull(getBackendConfigManager().getLocalBackendById(parentBackendID));
   }
 
   private BackendConfigManager getBackendConfigManager()
@@ -279,8 +502,6 @@
     return TestCaseUtils.getServerContext().getBackendConfigManager();
   }
 
-
-
   /**
    * 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.
@@ -290,38 +511,35 @@
   @Test
   public void testAddNestedBackendChildFirst() throws Exception
   {
+    BackendConfigManager manager = getBackendConfigManager();
     // Create the child backend and the corresponding base entry (at the time
     // of the creation, it will be a naming context).
     DN childBaseDN = DN.valueOf("ou=child,o=parent");
     String childBackendID = createBackendID(childBaseDN);
-    Entry childBackendEntry = createBackendEntry(childBackendID, true,
-                                                 childBaseDN);
+    Entry childBackendEntry = createBackendEntry(childBackendID, true, childBaseDN);
     processAdd(childBackendEntry);
 
-    LocalBackend<?> childBackend = getBackendConfigManager().getLocalBackend(childBackendID);
+    LocalBackend<?> childBackend = manager.getLocalBackendById(childBackendID);
     assertBackend(childBaseDN, childBackend);
     createEntry(childBaseDN, childBackend);
-    assertTrue(DirectoryServer.isNamingContext(childBaseDN));
+    assertTrue(manager.containsLocalNamingContext(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.valueOf("o=parent");
     String parentBackendID = createBackendID(parentBaseDN);
-    Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
-                                                  parentBaseDN);
+    Entry parentBackendEntry = createBackendEntry(parentBackendID, true, parentBaseDN);
     processAdd(parentBackendEntry);
 
-    LocalBackend<?> parentBackend = getBackendConfigManager().getLocalBackend(parentBackendID);
+    LocalBackend<?> parentBackend = manager.getLocalBackendById(parentBackendID);
     assertNotNull(parentBackend);
-    assertEquals(parentBackend, getBackendConfigManager().getLocalBackendWithBaseDN(parentBaseDN));
-    assertNotNull(childBackend.getParentBackend());
-    assertEquals(parentBackend, childBackend.getParentBackend());
-    assertEquals(parentBackend.getSubordinateBackends().length, 1);
+    assertEquals(parentBackend, manager.getLocalBackendWithBaseDN(parentBaseDN));
+    assertThat(manager.getSubordinateBackends(parentBackend)).containsExactly(childBackend);
 
     createEntry(parentBaseDN, parentBackend);
-    assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
-    assertFalse(DirectoryServer.isNamingContext(childBaseDN));
+    assertTrue(manager.containsLocalNamingContext(parentBaseDN));
+    assertFalse(manager.containsLocalNamingContext(childBaseDN));
 
 
     // Verify that we can see both entries with a subtree search.
@@ -334,12 +552,12 @@
     // Delete the backends from the server.
     DeleteOperation deleteOperation = getRootConnection().processDelete(childBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(childBackendID));
-    assertEquals(parentBackend.getSubordinateBackends().length, 0);
+    assertNull(manager.getLocalBackendById(childBackendID));
+    assertThat(manager.getSubordinateBackends(parentBackend)).isEmpty();
 
     deleteOperation = getRootConnection().processDelete(parentBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(parentBackendID));
+    assertNull(manager.getLocalBackendById(parentBackendID));
   }
 
   private void assertBackend(DN baseDN, LocalBackend<?> backend) throws DirectoryException
@@ -347,10 +565,8 @@
     assertNotNull(backend);
     assertEquals(backend, getBackendConfigManager().getLocalBackendWithBaseDN(baseDN));
     assertFalse(backend.entryExists(baseDN));
-    assertNull(backend.getParentBackend());
-    assertEquals(backend.getSubordinateBackends().length, 0);
-    assertFalse(backend.entryExists(baseDN));
-    assertTrue(DirectoryServer.isNamingContext(baseDN));
+    assertThat(getBackendConfigManager().getSubordinateBackends(backend)).isEmpty();
+    assertTrue(getBackendConfigManager().containsLocalNamingContext(baseDN));
   }
 
   /**
@@ -363,51 +579,47 @@
   @Test
   public void testInsertIntermediateBackend() throws Exception
   {
+    BackendConfigManager manager = getBackendConfigManager();
     // Add the parent backend to the server and its corresponding base entry.
     DN parentBaseDN = DN.valueOf("o=parent");
     String parentBackendID = createBackendID(parentBaseDN);
-    Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
-                                                  parentBaseDN);
+    Entry parentBackendEntry = createBackendEntry(parentBackendID, true, parentBaseDN);
     processAdd(parentBackendEntry);
 
-    LocalBackend<?> parentBackend = getBackendConfigManager().getLocalBackend(parentBackendID);
+    LocalBackend<?> parentBackend = manager.getLocalBackendById(parentBackendID);
     assertBackend(parentBaseDN, parentBackend);
     createEntry(parentBaseDN, parentBackend);
-    assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
-
+    assertTrue(getServerContext().getBackendConfigManager().containsLocalNamingContext(parentBaseDN));
 
     // Add the grandchild backend to the server.
     DN grandchildBaseDN = DN.valueOf("ou=grandchild,ou=child,o=parent");
     String grandchildBackendID = createBackendID(grandchildBaseDN);
-    Entry grandchildBackendEntry = createBackendEntry(grandchildBackendID, true,
-                                                      grandchildBaseDN);
+    Entry grandchildBackendEntry = createBackendEntry(grandchildBackendID, true, grandchildBaseDN);
     processAdd(grandchildBackendEntry);
 
-    LocalBackend<?> grandchildBackend = getBackendConfigManager().getLocalBackend(grandchildBackendID);
+    LocalBackend<?> grandchildBackend = manager.getLocalBackendById(grandchildBackendID);
     assertNotNull(grandchildBackend);
-    assertEquals(grandchildBackend, getBackendConfigManager().getLocalBackendWithBaseDN(grandchildBaseDN));
-    assertNotNull(grandchildBackend.getParentBackend());
-    assertEquals(grandchildBackend.getParentBackend(), parentBackend);
-    assertEquals(parentBackend.getSubordinateBackends().length, 1);
+    assertEquals(grandchildBackend, manager.getLocalBackendWithBaseDN(grandchildBaseDN));
+    assertThat(manager.getSubordinateBackends(parentBackend)).containsExactly(grandchildBackend);
     assertFalse(grandchildBackend.entryExists(grandchildBaseDN));
 
-    // Verify that we can't create the grandchild base entry because its parent
-    // doesn't exist.
+    // Verify that we can't create the grandchild base entry because its parent doesn't exist.
     Entry e = StaticUtils.createEntry(grandchildBaseDN);
     AddOperation addOperation = getRootConnection().processAdd(e);
     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.valueOf("ou=child,o=parent");
     String childBackendID = createBackendID(childBaseDN);
-    Entry childBackendEntry = createBackendEntry(childBackendID, true,
-                                                 childBaseDN);
+    Entry childBackendEntry = createBackendEntry(childBackendID, true, childBaseDN);
     processAdd(childBackendEntry);
 
-    LocalBackend<?> childBackend = getBackendConfigManager().getLocalBackend(childBackendID);
-    createBackend(childBaseDN, childBackend, parentBackend, grandchildBackend);
+    LocalBackend<?> childBackend = manager.getLocalBackendById(childBackendID);
+    assertThat(childBackend).isNotNull();
+    assertThat(manager.getLocalBackendWithBaseDN(childBaseDN)).isEqualTo(childBackend);
+    assertThat(manager.getSubordinateBackends(parentBackend)).containsExactly(childBackend);
+    assertThat(manager.getSubordinateBackends(childBackend)).containsExactly(grandchildBackend);
     createEntry(childBaseDN, childBackend);
 
     // Now we can create the grandchild base entry.
@@ -423,17 +635,17 @@
 
     assertSearchResultsSize(request, 2);
 
-
     // Re-enable the intermediate backend.
     enableBackend(childBackendEntry, true);
 
-
     // 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 = getBackendConfigManager().getLocalBackend(childBackendID);
-    createBackend(childBaseDN, childBackend, parentBackend, grandchildBackend);
-
+    childBackend = manager.getLocalBackendById(childBackendID);
+    assertNotNull(childBackend);
+    assertEquals(childBackend, manager.getLocalBackendWithBaseDN(childBaseDN));
+    assertThat(manager.getSubordinateBackends(parentBackend)).containsExactly(childBackend);
+    assertThat(manager.getSubordinateBackends(childBackend)).containsExactly(grandchildBackend);
 
     // 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
@@ -441,32 +653,28 @@
     // the entire chain of backends.
     assertSearchResultsSize(request, 2);
 
-
     // Add the child entry back into the server to get things back to the way
     // they were before we disabled the backend.
     createEntry(childBaseDN, childBackend);
 
-
     // We should again be able to see all three entries when performing a search
     assertSearchResultsSize(request, 3);
 
-
     // Get rid of the entries in the proper order.
-    DeleteOperation deleteOperation =
-         conn.processDelete(grandchildBackendEntry.getName());
+    DeleteOperation deleteOperation = conn.processDelete(grandchildBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(grandchildBackendID));
-    assertEquals(childBackend.getSubordinateBackends().length, 0);
-    assertEquals(parentBackend.getSubordinateBackends().length, 1);
+    assertNull(manager.getLocalBackendById(grandchildBackendID));
+    assertThat(manager.getSubordinateBackends(parentBackend)).containsExactly(childBackend);
+    assertThat(manager.getSubordinateBackends(childBackend)).isEmpty();
 
     deleteOperation = conn.processDelete(childBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(childBackendID));
-    assertEquals(parentBackend.getSubordinateBackends().length, 0);
+    assertNull(manager.getLocalBackendById(childBackendID));
+    assertThat(manager.getSubordinateBackends(parentBackend)).isEmpty();
 
     deleteOperation = conn.processDelete(parentBackendEntry.getName());
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
-    assertNull(getBackendConfigManager().getLocalBackend(parentBackendID));
+    assertNull(manager.getLocalBackendById(parentBackendID));
   }
 
   private void enableBackend(Entry entry, boolean enabled)
@@ -484,20 +692,6 @@
     assertEquals(internalSearch.getSearchEntries().size(), expected);
   }
 
-  private void createBackend(DN childBaseDN, LocalBackend<?> childBackend, LocalBackend<?> parentBackend,
-      LocalBackend<?> grandchildBackend) throws DirectoryException
-  {
-    assertNotNull(childBackend);
-    assertEquals(childBackend, getBackendConfigManager().getLocalBackendWithBaseDN(childBaseDN));
-    assertNotNull(childBackend.getParentBackend());
-    assertEquals(parentBackend, childBackend.getParentBackend());
-    assertEquals(parentBackend.getSubordinateBackends().length, 1);
-    assertFalse(childBackend.entryExists(childBaseDN));
-    assertEquals(childBackend.getSubordinateBackends().length, 1);
-    assertEquals(childBackend.getSubordinateBackends()[0], grandchildBackend);
-    assertEquals(grandchildBackend.getParentBackend(), childBackend);
-  }
-
   private void createEntry(DN baseDN, LocalBackend<?> backend) throws DirectoryException
   {
     Entry e = StaticUtils.createEntry(baseDN);
@@ -534,6 +728,7 @@
     lines.add("dn: ds-cfg-backend-id=" + backendID + ",cn=Backends,cn=config");
     lines.add("objectClass: top");
     lines.add("objectClass: ds-cfg-backend");
+    lines.add("objectClass: ds-cfg-local-backend");
     lines.add("objectClass: ds-cfg-memory-backend");
     lines.add("ds-cfg-backend-id: " + backendID);
     lines.add("ds-cfg-java-class: org.opends.server.backends.MemoryBackend");
@@ -588,5 +783,225 @@
 
     return buffer.toString();
   }
+
+  /** Mockito can not be used to provide a mock with a backend id because getBackendID() is final. */
+  static class BackendMock extends Backend<BackendCfg>
+  {
+    private final Set<DN> baseDNs;
+
+    BackendMock(String backendId, Set<DN> baseDNs)
+    {
+      this.baseDNs = baseDNs;
+      setBackendID(backendId);
+    }
+
+    @Override
+    public void configureBackend(BackendCfg cfg, ServerContext serverContext) throws ConfigException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void openBackend() throws ConfigException, InitializationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void finalizeBackend()
+    {
+      // do nothing
+    }
+
+    @Override
+    public Set<DN> getBaseDNs()
+    {
+      return baseDNs;
+    }
+
+    @Override
+    public boolean isDefaultRoute()
+    {
+      return false;
+    }
+
+    @Override
+    public Set<String> getSupportedControls()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Set<String> getSupportedFeatures()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public boolean isPrivateBackend()
+    {
+      return false;
+    }
+
+    @Override
+    public String toString()
+    {
+      return "BackendMock [backendId=" + getBackendID() + ", baseDNs=" + baseDNs + "]";
+    }
+  }
+
+  /** Mockito can not be used to provide a mock with a backend id because getBackendID() is final. */
+  static class LocalBackendMock extends LocalBackend<LocalBackendCfg>
+  {
+    private final Set<DN> baseDNs;
+
+    LocalBackendMock(String backendId, Set<DN> baseDNs)
+    {
+      this.baseDNs = baseDNs;
+      setBackendID(backendId);
+    }
+
+    @Override
+    public void openBackend() throws ConfigException, InitializationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public boolean isIndexed(AttributeType attributeType, IndexType indexType)
+    {
+      return false;
+    }
+
+    @Override
+    public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException
+    {
+      return null;
+    }
+
+    @Override
+    public long getNumberOfChildren(DN parentDN) throws DirectoryException
+    {
+      return 0;
+    }
+
+    @Override
+    public long getNumberOfEntriesInBaseDN(DN baseDN) throws DirectoryException
+    {
+      return 0;
+    }
+
+    @Override
+    public Entry getEntry(DN entryDN) throws DirectoryException
+    {
+      return null;
+    }
+
+    @Override
+    public void addEntry(Entry entry, AddOperation addOperation) throws DirectoryException, CanceledOperationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) throws DirectoryException,
+        CanceledOperationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void replaceEntry(Entry oldEntry, Entry newEntry, ModifyOperation modifyOperation) throws DirectoryException,
+        CanceledOperationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void renameEntry(DN currentDN, Entry entry, ModifyDNOperation modifyDNOperation) throws DirectoryException,
+        CanceledOperationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void search(SearchOperation searchOperation) throws DirectoryException, CanceledOperationException
+    {
+      // do nothing
+    }
+
+    @Override
+    public boolean supports(org.opends.server.api.LocalBackend.BackendOperation backendOperation)
+    {
+      return false;
+    }
+
+    @Override
+    public void exportLDIF(LDIFExportConfig exportConfig) throws DirectoryException
+    {
+      // do nothing
+    }
+
+    @Override
+    public LDIFImportResult importLDIF(LDIFImportConfig importConfig, ServerContext serverContext)
+        throws DirectoryException
+    {
+      return null;
+    }
+
+    @Override
+    public void createBackup(BackupConfig backupConfig) throws DirectoryException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void removeBackup(BackupDirectory backupDirectory, String backupID) throws DirectoryException
+    {
+      // do nothing
+    }
+
+    @Override
+    public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException
+    {
+      // do nothing
+    }
+
+    @Override
+    public long getEntryCount()
+    {
+      return 0;
+    }
+
+    @Override
+    public void configureBackend(LocalBackendCfg cfg, ServerContext serverContext) throws ConfigException
+    {
+      // do nothing
+    }
+
+    @Override
+    public Set<DN> getBaseDNs()
+    {
+      return baseDNs;
+    }
+
+    @Override
+    public Set<String> getSupportedControls()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public Set<String> getSupportedFeatures()
+    {
+      return Collections.emptySet();
+    }
+
+    @Override
+    public String toString()
+    {
+      return "LocalBackendMock [backendId=" + getBackendID() + ", baseDNs=" + baseDNs + "]";
+    }
+  }
 }
 
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/core/DeleteOperationTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/core/DeleteOperationTestCase.java
index b84b636..daa9c3b 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/core/DeleteOperationTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/core/DeleteOperationTestCase.java
@@ -59,7 +59,7 @@
   @AfterMethod(alwaysRun=true)
   public void reenableBackend() throws DirectoryException {
     LocalBackend<?> b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     b.setWritabilityMode(WritabilityMode.ENABLED);
   }
 
@@ -571,7 +571,7 @@
     TestCaseUtils.initializeTestBackend(true);
 
     LocalBackend<?> backend =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     backend.setWritabilityMode(WritabilityMode.DISABLED);
 
     DeleteOperation deleteOperation = processDeleteRaw("o=test");
@@ -594,7 +594,7 @@
     TestCaseUtils.initializeTestBackend(true);
 
     LocalBackend<?> backend =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     backend.setWritabilityMode(WritabilityMode.INTERNAL_ONLY);
 
     DeleteOperation deleteOperation = processDeleteRaw("o=test");
@@ -617,7 +617,7 @@
     TestCaseUtils.initializeTestBackend(true);
 
     LocalBackend<?> backend =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("o=test"));
     backend.setWritabilityMode(WritabilityMode.INTERNAL_ONLY);
 
     String[] args = getArgs("o=test");
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/core/ModifyOperationTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/core/ModifyOperationTestCase.java
index 3c1ff72..503d7e8 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/core/ModifyOperationTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/core/ModifyOperationTestCase.java
@@ -96,7 +96,7 @@
     for (Object[] backendBaseDN2 : getBaseDNs())
     {
       final DN baseDN = DN.valueOf(backendBaseDN2[0].toString());
-      LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(baseDN);
+      LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(baseDN);
       b.setWritabilityMode(WritabilityMode.ENABLED);
     }
   }
@@ -2429,7 +2429,8 @@
          "mail: foo",
          "employeeNumber: 1");
 
-    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf(baseDN));
+    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf(baseDN));
     b.setWritabilityMode(WritabilityMode.DISABLED);
 
     RawModification mod = newRawModification(ADD, "objectClass", "extensibleObject");
@@ -2467,7 +2468,8 @@
          "mail: foo",
          "employeeNumber: 1");
 
-    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf(baseDN));
+    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf(baseDN));
     b.setWritabilityMode(WritabilityMode.INTERNAL_ONLY);
 
     RawModification mod = newRawModification(ADD, "objectClass", "extensibleObject");
@@ -2505,7 +2507,8 @@
          "mail: foo",
          "employeeNumber: 1");
 
-    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf(baseDN));
+    LocalBackend<?> b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf(baseDN));
     b.setWritabilityMode(WritabilityMode.INTERNAL_ONLY);
 
     try (RemoteConnection conn = new RemoteConnection("localhost", TestCaseUtils.getServerLdapPort()))
@@ -3231,7 +3234,7 @@
         "userPassword: password",
         "userPassword;deleted: oldpassword");
     LocalBackend<?> backend =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(TEST_BACKEND_ID);
+        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById(TEST_BACKEND_ID);
     backend.addEntry(e, null); // Don't use add operation.
 
     // Constraint violation.
@@ -3273,7 +3276,7 @@
         "userPassword: password",
         "userPassword;deleted: oldpassword");
     LocalBackend<?> backend =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(TEST_BACKEND_ID);
+        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById(TEST_BACKEND_ID);
     backend.addEntry(e, null); // Don't use add operation.
 
     // Constraint violation.
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/extensions/CommonEntryCacheTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/extensions/CommonEntryCacheTestCase.java
index ef3a56f..f4452fd 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/extensions/CommonEntryCacheTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/extensions/CommonEntryCacheTestCase.java
@@ -94,8 +94,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     assertFalse(cache.containsEntry(testEntriesList.get(0).getName()),
       "Not expected to find " + testEntriesList.get(0).getName() +
@@ -129,8 +129,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     assertNull(cache.getEntry(testEntriesList.get(0).getName()),
       "Not expected to find " + testEntriesList.get(0).getName() +
@@ -164,8 +164,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     assertNull(cache.getEntry(testEntriesList.get(0).getName()),
       "Not expected to find " + testEntriesList.get(0).getName() +
@@ -199,8 +199,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     assertNull(cache.getEntry(b, -1),
       "Not expected to find entry id " + -1 +
@@ -233,8 +233,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     assertEquals(cache.getEntryID(testEntriesList.get(0).getName()), -1,
       "Not expected to find " + testEntriesList.get(0).getName() +
@@ -267,8 +267,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     cache.putEntry(testEntriesList.get(0), b, 1);
 
@@ -301,8 +301,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     assertTrue(cache.putEntryIfAbsent(testEntriesList.get(0), b, 1),
       "Not expected to find " + testEntriesList.get(0).getName() +
@@ -343,8 +343,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     cache.removeEntry(testEntriesList.get(0).getName());
     cache.putEntry(testEntriesList.get(0), b, 1);
@@ -379,8 +379,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     cache.clear();
     cache.putEntry(testEntriesList.get(0), b, 1);
@@ -415,11 +415,10 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
-    String c =
-        TestCaseUtils.getServerContext().getBackendConfigManager()
-          .getLocalBackend(DN.valueOf("cn=config")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
+    String c = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("cn=config")).getBackendID();
 
     cache.clearBackend(b);
     cache.putEntry(testEntriesList.get(0), b, 1);
@@ -445,55 +444,6 @@
     cache.clear();
   }
 
-
-
-  /**
-   * Tests the <CODE>clearSubtree</CODE> method.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
-   */
-  public void testClearSubtree()
-         throws Exception
-  {
-    assertNull(cache.toVerboseString(),
-      "Expected empty cache.  " + "Cache contents:" + ServerConstants.EOL +
-      cache.toVerboseString());
-
-    TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
-    String c = TestCaseUtils.getServerContext().getBackendConfigManager()
-        .getLocalBackend(DN.valueOf("cn=config")).getBackendID();
-
-    cache.putEntry(testEntriesList.get(0), b, 1);
-    Entry testEntry = testEntriesList.get(1);
-    testEntry.getName();
-    testEntry.setDN(DN.valueOf(
-      testEntry.getName().rdn() + ",cn=config"));
-    cache.putEntry(testEntry, c, 1);
-    cache.clearSubtree(DN.valueOf("o=test"));
-
-    assertNull(cache.getEntry(testEntriesList.get(0).getName()),
-      "Not expected to find " + testEntriesList.get(0).getName() +
-      " in the cache.  Cache contents:" + ServerConstants.EOL +
-      cache.toVerboseString());
-
-    assertNull(cache.getEntry(b, 1),
-      "Not expected to find entry id " + -1 +
-      " in the cache.  Cache contents:" + ServerConstants.EOL +
-      cache.toVerboseString());
-
-    assertNotNull(cache.getEntry(testEntry.getName()),
-      "Expected to find " + testEntry.getName() +
-      " in the cache.  Cache contents:" + ServerConstants.EOL +
-      cache.toVerboseString());
-
-    // Clear the cache so that other tests can start from scratch.
-    cache.clear();
-  }
-
-
-
   /**
    * Tests the <CODE>handleLowMemory</CODE> method.
    *
@@ -523,8 +473,8 @@
   public void testCacheConcurrency()
          throws Exception
   {
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     for(int loops = 0; loops < CONCURRENCYLOOPS; loops++) {
       for(int i = 0; i < NUMTESTENTRIES; i++) {
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/extensions/DefaultEntryCacheTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/extensions/DefaultEntryCacheTestCase.java
index aa87ccc..4dc7a69 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/extensions/DefaultEntryCacheTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/extensions/DefaultEntryCacheTestCase.java
@@ -91,7 +91,7 @@
       "ds-cfg-include-filter: uid=softref*",
       "ds-cfg-include-filter: uid=test1*",
       "ds-cfg-exclude-filter: uid=test0*");
-    softRefCache.initializeEntryCache(InitializationUtils.getConfiguration(
+    softRefCache.initializeEntryCache(TestCaseUtils.getServerContext(), InitializationUtils.getConfiguration(
       SoftReferenceEntryCacheCfgDefn.getInstance(), cacheSoftReferenceConfigEntry));
     cacheOrderMap.put(1, softRefCache);
 
@@ -108,7 +108,7 @@
       "ds-cfg-include-filter: uid=fifo*",
       "ds-cfg-include-filter: uid=test2*",
       "ds-cfg-include-filter: uid=test0*");
-    fifoCache.initializeEntryCache(InitializationUtils.getConfiguration(
+    fifoCache.initializeEntryCache(TestCaseUtils.getServerContext(), InitializationUtils.getConfiguration(
       FIFOEntryCacheCfgDefn.getInstance(), cacheFIFOConfigEntry));
     cacheOrderMap.put(2, fifoCache);
 
@@ -321,30 +321,6 @@
     super.testClearBackend();
   }
 
-
-
-  /** {@inheritDoc} */
-  @Test
-  @Override
-  public void testClearSubtree()
-         throws Exception
-  {
-    super.testClearSubtree();
-  }
-
-
-
-  /** {@inheritDoc} */
-  @Test
-  @Override
-  public void testHandleLowMemory()
-         throws Exception
-  {
-    super.testHandleLowMemory();
-  }
-
-
-
   /**
    * Tests the entry cache level functionality where each set
    * of entries land on a specific cache level by some form
@@ -361,8 +337,8 @@
       cache.toVerboseString());
 
     TestCaseUtils.initializeTestBackend(false);
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     // Spread test entries among all cache levels via default cache.
     for (int i = 0; i < NUMTESTENTRIES; i++) {
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/extensions/FIFOEntryCacheTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/extensions/FIFOEntryCacheTestCase.java
index 9b7e866..4b27006 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/extensions/FIFOEntryCacheTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/extensions/FIFOEntryCacheTestCase.java
@@ -71,7 +71,7 @@
 
     // Initialize the cache.
     super.cache = new FIFOEntryCache();
-    super.cache.initializeEntryCache(configuration);
+    super.cache.initializeEntryCache(TestCaseUtils.getServerContext(), configuration);
 
     // Make some dummy test entries.
     super.testEntriesList = new ArrayList<>(super.NUMTESTENTRIES);
@@ -228,19 +228,6 @@
     super.testClearBackend();
   }
 
-
-
-  /** {@inheritDoc} */
-  @Test
-  @Override
-  public void testClearSubtree()
-         throws Exception
-  {
-    super.testClearSubtree();
-  }
-
-
-
   /** {@inheritDoc} */
   @Test
   @Override
@@ -251,8 +238,8 @@
       "Expected empty cache.  " + "Cache contents:" + ServerConstants.EOL +
       cache.toVerboseString());
 
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     for(int i = 0; i < super.NUMTESTENTRIES; i++ ) {
       super.cache.putEntry(super.testEntriesList.get(i), b, i);
@@ -322,8 +309,8 @@
       "Expected empty cache.  " + "Cache contents:" + ServerConstants.EOL +
       cache.toVerboseString());
 
-    String b =
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("o=test")).getBackendID();
+    String b = TestCaseUtils.getServerContext().getBackendConfigManager()
+        .findLocalBackendForEntry(DN.valueOf("o=test")).getBackendID();
 
     for(int i = 0; i < super.NUMTESTENTRIES; i++ ) {
       super.cache.putEntry(super.testEntriesList.get(i), b, i);
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java
index 6349a9f..7b7aac7 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java
@@ -68,7 +68,7 @@
 
     // Initialize the cache.
     super.cache = new SoftReferenceEntryCache();
-    super.cache.initializeEntryCache(configuration);
+    super.cache.initializeEntryCache(TestCaseUtils.getServerContext(), configuration);
 
     // Make some dummy test entries.
     super.testEntriesList = new ArrayList<>(super.NUMTESTENTRIES);
@@ -225,19 +225,6 @@
     super.testClearBackend();
   }
 
-
-
-  /** {@inheritDoc} */
-  @Test
-  @Override
-  public void testClearSubtree()
-         throws Exception
-  {
-    super.testClearSubtree();
-  }
-
-
-
   /** {@inheritDoc} */
   @Test
   @Override
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LDAPBinaryOptionTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LDAPBinaryOptionTestCase.java
index a4d80d8..e63773e 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LDAPBinaryOptionTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LDAPBinaryOptionTestCase.java
@@ -440,7 +440,7 @@
     }
     exportConfig = new LDIFExportConfig(ldif.getAbsolutePath(),
                               ExistingFileBehavior.OVERWRITE);
-    backend = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend("test");
+    backend = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById("test");
     backend.exportLDIF(exportConfig);
   }
 
@@ -454,7 +454,7 @@
   {
     importConfig = new LDIFImportConfig(ldif.getAbsolutePath());
     TestCaseUtils.initializeTestBackend(false);
-    backend = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend("test");
+    backend = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById("test");
     backend.importLDIF(importConfig, DirectoryServer.getInstance().getServerContext());
   }
 }
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/DependencyTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/DependencyTest.java
index 0dc873e..8f3ab57 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/DependencyTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/DependencyTest.java
@@ -321,7 +321,7 @@
          "entryuuid: " + stringUID(1));
 
     MemoryBackend memoryBackend =
-        (MemoryBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(TEST_BACKEND_ID);
+        (MemoryBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById(TEST_BACKEND_ID);
     memoryBackend.addEntry(topEntry, null);
   }
 
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/GenerationIdTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/GenerationIdTest.java
index d6af00f..b2d6f3e 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/GenerationIdTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/GenerationIdTest.java
@@ -491,7 +491,7 @@
     LDIFImportConfig importConfig = new LDIFImportConfig(ldifFile.getAbsolutePath());
 
     MemoryBackend memoryBackend =
-        (MemoryBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(TEST_BACKEND_ID);
+        (MemoryBackend) TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById(TEST_BACKEND_ID);
     memoryBackend.importLDIF(importConfig, DirectoryServer.getInstance().getServerContext());
   }
 
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/tasks/TasksTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/tasks/TasksTestCase.java
index 86a972a..44517f2 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/tasks/TasksTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/tasks/TasksTestCase.java
@@ -109,7 +109,7 @@
   public static Task getTask(final DN taskEntryDN) throws Exception
   {
     final TaskBackend taskBackend = (TaskBackend)
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("cn=tasks"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("cn=tasks"));
 
     TestTimer timer = new TestTimer.Builder()
       .maxSleep(10, SECONDS)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/types/PrivilegeTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/types/PrivilegeTestCase.java
index 41e84dd..618dad7 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/types/PrivilegeTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/types/PrivilegeTestCase.java
@@ -2378,7 +2378,7 @@
   private Task getCompletedTask(DN taskEntryDN) throws Exception
   {
     TaskBackend taskBackend = (TaskBackend)
-        TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackend(DN.valueOf("cn=tasks"));
+        TestCaseUtils.getServerContext().getBackendConfigManager().findLocalBackendForEntry(DN.valueOf("cn=tasks"));
     Task task = taskBackend.getScheduledTask(taskEntryDN);
     if (task == null)
     {
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/types/TestDN.java b/opendj-server-legacy/src/test/java/org/opends/server/types/TestDN.java
index a105724..afbe1cd 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/types/TestDN.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/types/TestDN.java
@@ -16,6 +16,9 @@
  */
 package org.opends.server.types;
 
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PRIVATE;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.PUBLIC;
+import static org.opends.server.core.BackendConfigManager.NamingContextFilter.TOP_LEVEL;
 import static org.testng.Assert.*;
 
 import java.util.ArrayList;
@@ -23,6 +26,7 @@
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.RDN;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.core.BackendConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -39,9 +43,10 @@
   @DataProvider(name = "namingContexts")
   public Object[][] getNamingContexts() {
     ArrayList<DN> contextList = new ArrayList<>();
-    contextList.addAll(DirectoryServer.getPublicNamingContexts().keySet());
-    contextList.addAll(DirectoryServer.getInstance().getServerContext().getBackendConfigManager().
-        getPrivateNamingContexts().keySet());
+    BackendConfigManager manager = DirectoryServer.getInstance().getServerContext().getBackendConfigManager();
+    contextList.addAll(manager.getNamingContexts(PRIVATE));
+    contextList.addAll(manager.getNamingContexts(PUBLIC));
+    contextList.addAll(manager.getNamingContexts(PUBLIC, TOP_LEVEL));
 
     Object[][] contextArray = new Object[contextList.size()][1];
     for (int i = 0;i < contextArray.length;i++) {
@@ -53,11 +58,12 @@
 
   @Test(dataProvider = "namingContexts")
   public void testGetParentDNInSuffix(DN namingContext) throws Exception {
-    assertNull(DirectoryServer.getParentDNInSuffix(namingContext));
+    BackendConfigManager backendConfigManager = TestCaseUtils.getServerContext().getBackendConfigManager();
+    assertNull(backendConfigManager.getParentDNInSuffix(namingContext));
 
     DN childDN = namingContext.child(RDN.valueOf("ou=People"));
-    assertNotNull(DirectoryServer.getParentDNInSuffix(childDN));
-    assertEquals(DirectoryServer.getParentDNInSuffix(childDN), namingContext);
+    assertNotNull(backendConfigManager.getParentDNInSuffix(childDN));
+    assertEquals(backendConfigManager.getParentDNInSuffix(childDN), namingContext);
   }
 }
 

--
Gitblit v1.10.0