mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Nicolas Capponi
14.53.2016 97050c54efee9d0cc89afb5fb61b9a1c67f73f6a
OPENDJ-3460 Improve management of subordinate backend and naming contexts

- Remove backend parent/subordinate relationships from LocalBackend class
- Manage backends parent/subordinate relationships in BackendConfigManager class
- Generalize naming context information to all backends, not only local ones
1 files renamed
67 files modified
3534 ■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/api/EntryCache.java 15 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/api/LocalBackend.java 116 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/BackupBackend.java 14 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java 8 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/LDIFBackend.java 26 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/MemoryBackend.java 10 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/RootDSEBackend.java 31 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/TrustStoreBackend.java 8 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ImportLDIFReader.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/RootContainer.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskBackend.java 12 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/task/TaskScheduler.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationBasis.java 6 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java 1903 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java 82 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/EntryCacheConfigManager.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationBasis.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/PersistentSearch.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/DefaultEntryCache.java 11 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/ExactMatchIdentityMapper.java 22 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/FIFOEntryCache.java 108 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/FingerprintCertificateMapper.java 18 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java 12 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java 12 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/PasswordModifyExtendedOperation.java 7 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/RegularExpressionIdentityMapper.java 22 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/SoftReferenceEntryCache.java 20 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectAttributeToUserAttributeCertificateMapper.java 9 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/extensions/SubjectDNToUserAttributeCertificateMapper.java 13 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java 16 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/plugins/SevenBitCleanPlugin.java 5 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/plugins/UniqueAttributePlugin.java 29 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/FakeAddOperation.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java 11 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/MultimasterReplication.java 5 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tasks/BackupTask.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tasks/ExportTask.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java 29 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java 6 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java 7 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java 18 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java 8 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 19 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java 6 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/GenericLocalBackendTestCase.java 18 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/LDIFBackendTestCase.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/task/TaskBackendTestCase.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/core/AddOperationTestCase.java 8 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/core/BackendConfigManagerTestCase.java 593 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/core/DeleteOperationTestCase.java 8 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/core/ModifyOperationTestCase.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/extensions/CommonEntryCacheTestCase.java 98 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/extensions/DefaultEntryCacheTestCase.java 32 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/extensions/FIFOEntryCacheTestCase.java 23 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/extensions/SoftReferenceEntryCacheTestCase.java 15 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LDAPBinaryOptionTestCase.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/DependencyTest.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/GenerationIdTest.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/tasks/TasksTestCase.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/types/PrivilegeTestCase.java 2 ●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/types/TestDN.java 18 ●●●●● patch | view | raw | blame | history
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
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;
  }
  /**
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);
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");
  }
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(
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 =
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.
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);
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;
  }
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;
  }
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);
      }
    }
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. */
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);
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());
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;
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;
    }
  }
}
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);
  }
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"
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
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:
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));
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) {
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(
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.
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(
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();
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());
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)
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(
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
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;
  }
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
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)
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);
  }
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(
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());
  }
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;
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;
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);
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)
    {
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)
      {
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);
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
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())
        {
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());
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.
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);
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)
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));
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)
    {
opendj-server-legacy/src/test/java/org/opends/server/backends/GenericLocalBackendTestCase.java
File was renamed from opendj-server-legacy/src/test/java/org/opends/server/backends/GenericBackendTestCase.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.
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;
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);
  }
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));
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();
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 + "]";
    }
  }
}
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");
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.
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++) {
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++) {
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);
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
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());
  }
}
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);
  }
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());
  }
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)
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)
    {
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);
  }
}