From d75c93322e20bdad4cc2c57bd784a03d95fd3105 Mon Sep 17 00:00:00 2001
From: kenneth_suter <kenneth_suter@localhost>
Date: Mon, 24 Sep 2007 12:15:23 +0000
Subject: [PATCH] Creation of a base DN registry for managing the base DN and naming context to backend maps.  Supports a 'test' mode that can be used to test whether registration/deregistration operations will be acceptable.

---
 opends/src/server/org/opends/server/core/BackendConfigManager.java                                 |   14 
 opends/src/server/org/opends/server/core/BaseDnRegistry.java                                       |  516 ++++++++++++++++++++++++++++++
 opends/src/server/org/opends/server/backends/BackupBackend.java                                    |    4 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java |    2 
 opends/src/server/org/opends/server/backends/LDIFBackend.java                                      |    4 
 opends/src/server/org/opends/server/backends/MemoryBackend.java                                    |    4 
 opends/src/server/org/opends/server/core/DirectoryServer.java                                      |  396 +++--------------------
 opends/src/server/org/opends/server/backends/MonitorBackend.java                                   |    4 
 opends/src/server/org/opends/server/backends/jeb/BackendImpl.java                                  |    8 
 opends/src/server/org/opends/server/backends/TrustStoreBackend.java                                |    4 
 opends/src/server/org/opends/server/extensions/ConfigFileHandler.java                              |    5 
 opends/src/server/org/opends/server/replication/server/ReplicationBackend.java                     |    4 
 opends/src/server/org/opends/server/backends/SchemaBackend.java                                    |    8 
 opends/src/server/org/opends/server/backends/task/TaskBackend.java                                 |    4 
 14 files changed, 602 insertions(+), 375 deletions(-)

diff --git a/opends/src/server/org/opends/server/backends/BackupBackend.java b/opends/src/server/org/opends/server/backends/BackupBackend.java
index 0f7332b..6e22fce 100644
--- a/opends/src/server/org/opends/server/backends/BackupBackend.java
+++ b/opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -213,7 +213,7 @@
     // Register the backup base as a private suffix.
     try
     {
-      DirectoryServer.registerBaseDN(backupBaseDN, this, true, false);
+      DirectoryServer.registerBaseDN(backupBaseDN, this, true);
     }
     catch (Exception e)
     {
@@ -247,7 +247,7 @@
 
     try
     {
-      DirectoryServer.deregisterBaseDN(backupBaseDN, false);
+      DirectoryServer.deregisterBaseDN(backupBaseDN);
     }
     catch (Exception e)
     {
diff --git a/opends/src/server/org/opends/server/backends/LDIFBackend.java b/opends/src/server/org/opends/server/backends/LDIFBackend.java
index 98d81d7..17f9cf3 100644
--- a/opends/src/server/org/opends/server/backends/LDIFBackend.java
+++ b/opends/src/server/org/opends/server/backends/LDIFBackend.java
@@ -170,7 +170,7 @@
     {
       try
       {
-        DirectoryServer.registerBaseDN(dn, this, false, false);
+        DirectoryServer.registerBaseDN(dn, this, false);
       }
       catch (Exception e)
       {
@@ -375,7 +375,7 @@
       {
         try
         {
-          DirectoryServer.deregisterBaseDN(dn, false);
+          DirectoryServer.deregisterBaseDN(dn);
         }
         catch (Exception e)
         {
diff --git a/opends/src/server/org/opends/server/backends/MemoryBackend.java b/opends/src/server/org/opends/server/backends/MemoryBackend.java
index 4060aac..2541f93 100644
--- a/opends/src/server/org/opends/server/backends/MemoryBackend.java
+++ b/opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -203,7 +203,7 @@
     {
       try
       {
-        DirectoryServer.registerBaseDN(dn, this, false, false);
+        DirectoryServer.registerBaseDN(dn, this, false);
       }
       catch (Exception e)
       {
@@ -243,7 +243,7 @@
     {
       try
       {
-        DirectoryServer.deregisterBaseDN(dn, false);
+        DirectoryServer.deregisterBaseDN(dn);
       }
       catch (Exception e)
       {
diff --git a/opends/src/server/org/opends/server/backends/MonitorBackend.java b/opends/src/server/org/opends/server/backends/MonitorBackend.java
index f820978..15678b6 100644
--- a/opends/src/server/org/opends/server/backends/MonitorBackend.java
+++ b/opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -247,7 +247,7 @@
     // Register the monitor base as a private suffix.
     try
     {
-      DirectoryServer.registerBaseDN(baseMonitorDN, this, true, false);
+      DirectoryServer.registerBaseDN(baseMonitorDN, this, true);
     }
     catch (Exception e)
     {
@@ -281,7 +281,7 @@
 
     try
     {
-      DirectoryServer.deregisterBaseDN(baseMonitorDN, false);
+      DirectoryServer.deregisterBaseDN(baseMonitorDN);
     }
     catch (Exception e)
     {
diff --git a/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opends/src/server/org/opends/server/backends/SchemaBackend.java
index 46ae527..565440e 100644
--- a/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -366,7 +366,7 @@
     {
       try
       {
-        DirectoryServer.registerBaseDN(baseDNs[i], this, true, false);
+        DirectoryServer.registerBaseDN(baseDNs[i], this, true);
       }
       catch (Exception e)
       {
@@ -514,7 +514,7 @@
     {
       try
       {
-        DirectoryServer.deregisterBaseDN(baseDN, false);
+        DirectoryServer.deregisterBaseDN(baseDN);
       }
       catch (Exception e)
       {
@@ -5156,7 +5156,7 @@
       {
         try
         {
-          DirectoryServer.deregisterBaseDN(dn, false);
+          DirectoryServer.deregisterBaseDN(dn);
           messages.add(INFO_SCHEMA_DEREGISTERED_BASE_DN.get(
                   String.valueOf(dn)));
         }
@@ -5179,7 +5179,7 @@
       {
         try
         {
-          DirectoryServer.registerBaseDN(dn, this, true, false);
+          DirectoryServer.registerBaseDN(dn, this, true);
           messages.add(INFO_SCHEMA_REGISTERED_BASE_DN.get(String.valueOf(dn)));
         }
         catch (Exception e)
diff --git a/opends/src/server/org/opends/server/backends/TrustStoreBackend.java b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
index 00f2d5a..4c85335 100644
--- a/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
+++ b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -366,7 +366,7 @@
     // Register the trust store base as a private suffix.
     try
     {
-      DirectoryServer.registerBaseDN(baseDN, this, true, false);
+      DirectoryServer.registerBaseDN(baseDN, this, true);
     }
     catch (Exception e)
     {
@@ -392,7 +392,7 @@
 
     try
     {
-      DirectoryServer.deregisterBaseDN(baseDN, false);
+      DirectoryServer.deregisterBaseDN(baseDN);
     }
     catch (Exception e)
     {
diff --git a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index fc4ee52..0b7bcf7 100644
--- a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -340,7 +340,7 @@
     {
       try
       {
-        DirectoryServer.registerBaseDN(dn, this, false, false);
+        DirectoryServer.registerBaseDN(dn, this, false);
       }
       catch (Exception e)
       {
@@ -386,7 +386,7 @@
     {
       try
       {
-        DirectoryServer.deregisterBaseDN(dn, false);
+        DirectoryServer.deregisterBaseDN(dn);
       }
       catch (Exception e)
       {
@@ -1571,7 +1571,7 @@
           if (!found)
           {
             // The base DN was deleted.
-            DirectoryServer.deregisterBaseDN(baseDN, false);
+            DirectoryServer.deregisterBaseDN(baseDN);
             EntryContainer ec =
                 rootContainer.unregisterEntryContainer(baseDN);
             ec.delete();
@@ -1588,7 +1588,7 @@
               EntryContainer ec =
                   rootContainer.openEntryContainer(baseDN, null);
               rootContainer.registerEntryContainer(baseDN, ec);
-              DirectoryServer.registerBaseDN(baseDN, this, false, false);
+              DirectoryServer.registerBaseDN(baseDN, this, false);
             }
             catch (Exception e)
             {
diff --git a/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index afe3b7c..6dccc78 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -255,7 +255,7 @@
     // Register the task base as a private suffix.
     try
     {
-      DirectoryServer.registerBaseDN(taskRootDN, this, true, false);
+      DirectoryServer.registerBaseDN(taskRootDN, this, true);
     }
     catch (Exception e)
     {
@@ -318,7 +318,7 @@
 
     try
     {
-      DirectoryServer.deregisterBaseDN(taskRootDN, false);
+      DirectoryServer.deregisterBaseDN(taskRootDN);
     }
     catch (Exception e)
     {
diff --git a/opends/src/server/org/opends/server/core/BackendConfigManager.java b/opends/src/server/org/opends/server/core/BackendConfigManager.java
index daab51b..2f272e7 100644
--- a/opends/src/server/org/opends/server/core/BackendConfigManager.java
+++ b/opends/src/server/org/opends/server/core/BackendConfigManager.java
@@ -402,11 +402,14 @@
         }
       }
 
-      for (DN dn : addedDNs)
+      // Copy the directory server's base DN registry and make the
+      // requested changes to see if it complains.
+      BaseDnRegistry reg = DirectoryServer.copyBaseDnRegistry();
+      for (DN dn : removedDNs)
       {
         try
         {
-          DirectoryServer.registerBaseDN(dn, backend, false, true);
+          reg.deregisterBaseDN(dn);
         }
         catch (DirectoryException de)
         {
@@ -420,11 +423,11 @@
         }
       }
 
-      for (DN dn : removedDNs)
+      for (DN dn : addedDNs)
       {
         try
         {
-          DirectoryServer.deregisterBaseDN(dn, true);
+          reg.registerBaseDN(dn, backend, false);
         }
         catch (DirectoryException de)
         {
@@ -928,11 +931,12 @@
 
 
     // Make sure that all of the base DNs are acceptable for use in the server.
+    BaseDnRegistry reg = DirectoryServer.copyBaseDnRegistry();
     for (DN baseDN : baseDNs)
     {
       try
       {
-        DirectoryServer.registerBaseDN(baseDN, backend, false, true);
+        reg.registerBaseDN(baseDN, backend, false);
       }
       catch (DirectoryException de)
       {
diff --git a/opends/src/server/org/opends/server/core/BaseDnRegistry.java b/opends/src/server/org/opends/server/core/BaseDnRegistry.java
new file mode 100644
index 0000000..8303f09
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/BaseDnRegistry.java
@@ -0,0 +1,516 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.core;
+
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.api.Backend;
+import static org.opends.server.util.Validator.ensureNotNull;
+import org.opends.messages.Message;
+import static org.opends.messages.CoreMessages.*;
+
+import java.util.TreeMap;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * Registry for maintaining the set of registered base DN's, assocated
+ * backends and naming context information.
+ */
+public class BaseDnRegistry {
+
+  // The set of base DNs registered with the server.
+  private TreeMap<DN,Backend> baseDNs;
+
+  // The set of private naming contexts registered with the server.
+  private TreeMap<DN,Backend> privateNamingContexts;
+
+  // The set of public naming contexts registered with the server.
+  private TreeMap<DN,Backend> publicNamingContexts;
+
+  // Indicates whether or not 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.
+  private boolean testOnly;
+
+  /**
+   * Registers a base DN with this registry.
+   *
+   * @param  baseDN to register
+   * @param  backend with which the base DN is assocated
+   * @param  isPrivate indicates whether or not 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
+   */
+  public List<Message> registerBaseDN(DN baseDN, Backend backend,
+                                      boolean isPrivate)
+          throws DirectoryException
+  {
+
+    List<Message> errors = new LinkedList<Message>();
+
+    // Check to see if the base DN is already registered with the server.
+    Backend existingBackend = baseDNs.get(baseDN);
+    if (existingBackend != null)
+    {
+      Message message = ERR_REGISTER_BASEDN_ALREADY_EXISTS.
+          get(String.valueOf(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
+    // relationship with any other base Dns for the same backend.
+    LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
+    for (DN dn : baseDNs.keySet())
+    {
+      Backend b = baseDNs.get(dn);
+      if (b.equals(backend))
+      {
+        otherBaseDNs.add(dn);
+
+        if (baseDN.isAncestorOf(dn) || baseDN.isDescendantOf(dn))
+        {
+          Message message = ERR_REGISTER_BASEDN_HIERARCHY_CONFLICT.
+              get(String.valueOf(baseDN), backend.getBackendID(),
+                  String.valueOf(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
+    // backend must also be subordinate to the same base DN.
+    Backend superiorBackend = null;
+    DN      superiorBaseDN        ;
+    DN      parentDN        = baseDN.getParent();
+    while (parentDN != null)
+    {
+      if (baseDNs.containsKey(parentDN))
+      {
+        superiorBaseDN  = parentDN;
+        superiorBackend = baseDNs.get(parentDN);
+
+        for (DN dn : otherBaseDNs)
+        {
+          if (! dn.isDescendantOf(superiorBaseDN))
+          {
+            Message message = ERR_REGISTER_BASEDN_DIFFERENT_PARENT_BASES.
+                get(String.valueOf(baseDN), backend.getBackendID(),
+                    String.valueOf(dn));
+            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+                                         message);
+          }
+        }
+
+        break;
+      }
+
+      parentDN = parentDN.getParent();
+    }
+
+    if (superiorBackend == null)
+    {
+      if (backend.getParentBackend() != null)
+      {
+        Message message = ERR_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE.
+            get(String.valueOf(baseDN), backend.getBackendID(),
+                backend.getParentBackend().getBackendID());
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+                                     message);
+      }
+    }
+
+
+    // Check to see if the new base DN should be the superior base DN for any
+    // other base DN(s) already defined.
+    LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
+    LinkedList<DN>      subordinateBaseDNs  = new LinkedList<DN>();
+    for (DN dn : baseDNs.keySet())
+    {
+      Backend b = baseDNs.get(dn);
+      parentDN = dn.getParent();
+      while (parentDN != null)
+      {
+        if (parentDN.equals(baseDN))
+        {
+          subordinateBaseDNs.add(dn);
+          subordinateBackends.add(b);
+          break;
+        }
+        else if (baseDNs.containsKey(parentDN))
+        {
+          break;
+        }
+
+        parentDN = parentDN.getParent();
+      }
+    }
+
+
+    // If we've gotten here, then the new base DN is acceptable.  If we should
+    // actually apply the changes then do so now.
+
+    // Check to see if any of the registered backends already contain an
+    // entry with the DN specified as the base DN.  This could happen if
+    // we're creating a new subordinate backend in an existing directory
+    // (e.g., moving the "ou=People,dc=example,dc=com" branch to its own
+    // backend when that data already exists under the "dc=example,dc=com"
+    // backend).  This condition shouldn't prevent the new base DN from
+    // being registered, but it's definitely important enough that we let
+    // the administrator know about it and remind them that the existing
+    // backend will need to be reinitialized.
+    if (superiorBackend != null)
+    {
+      if (superiorBackend.entryExists(baseDN))
+      {
+        Message message = WARN_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS.
+            get(superiorBackend.getBackendID(), String.valueOf(baseDN),
+                backend.getBackendID());
+        errors.add(message);
+      }
+    }
+
+
+    baseDNs.put(baseDN, backend);
+
+    if (superiorBackend == null)
+    {
+      if (isPrivate)
+      {
+        if (!testOnly)
+        {
+          backend.setPrivateBackend(true);
+        }
+        privateNamingContexts.put(baseDN, backend);
+      }
+      else
+      {
+        if (!testOnly)
+        {
+          backend.setPrivateBackend(false);
+        }
+        publicNamingContexts.put(baseDN, backend);
+      }
+    }
+    else if (otherBaseDNs.isEmpty())
+    {
+      if (!testOnly)
+      {
+        backend.setParentBackend(superiorBackend);
+        superiorBackend.addSubordinateBackend(backend);
+      }
+    }
+
+    if (!testOnly)
+    {
+      for (Backend b : subordinateBackends)
+      {
+        Backend oldParentBackend = b.getParentBackend();
+        if (oldParentBackend != null)
+        {
+          oldParentBackend.removeSubordinateBackend(b);
+        }
+
+        b.setParentBackend(backend);
+        backend.addSubordinateBackend(b);
+      }
+    }
+
+    for (DN dn : subordinateBaseDNs)
+    {
+      publicNamingContexts.remove(dn);
+      privateNamingContexts.remove(dn);
+    }
+
+    return errors;
+  }
+
+
+  /**
+   * 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
+   */
+  public List<Message> deregisterBaseDN(DN baseDN)
+         throws DirectoryException
+  {
+    LinkedList<Message> errors = new LinkedList<Message>();
+
+    ensureNotNull(baseDN);
+
+    // Make sure that the Directory Server actually contains a backend with
+    // the specified base DN.
+    Backend backend = baseDNs.get(baseDN);
+    if (backend == null)
+    {
+      Message message =
+          ERR_DEREGISTER_BASEDN_NOT_REGISTERED.get(String.valueOf(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.
+    Backend             superiorBackend     = backend.getParentBackend();
+    LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
+    if (backend.getSubordinateBackends() != null)
+    {
+      for (Backend b : backend.getSubordinateBackends())
+      {
+        for (DN dn : b.getBaseDNs())
+        {
+          if (dn.isDescendantOf(baseDN))
+          {
+            subordinateBackends.add(b);
+            break;
+          }
+        }
+      }
+    }
+
+
+    // See if there are any other base DNs registered within the same backend.
+    LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
+    for (DN dn : baseDNs.keySet())
+    {
+      if (dn.equals(baseDN))
+      {
+        continue;
+      }
+
+      Backend 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);
+    privateNamingContexts.remove(baseDN);
+
+    if (superiorBackend == null)
+    {
+      // If there were any subordinate backends, then all of their base DNs
+      // will now be promoted to naming contexts.
+      for (Backend b : subordinateBackends)
+      {
+        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);
+          }
+        }
+      }
+    }
+    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())
+      {
+        if (!testOnly)
+        {
+          superiorBackend.removeSubordinateBackend(backend);
+        }
+      }
+
+
+      // If there are any subordinate backends, then they need to be made
+      // subordinate to the parent backend.  Also, we should log a warning
+      // message indicating that there may be inconsistent search results
+      // because some of the structural entries will be missing.
+      if (! subordinateBackends.isEmpty())
+      {
+        Message message = WARN_DEREGISTER_BASEDN_MISSING_HIERARCHY.get(
+            String.valueOf(baseDN), backend.getBackendID());
+        errors.add(message);
+
+        if (!testOnly)
+        {
+          for (Backend b : subordinateBackends)
+          {
+            backend.removeSubordinateBackend(b);
+            superiorBackend.addSubordinateBackend(b);
+            b.setParentBackend(superiorBackend);
+          }
+        }
+      }
+    }
+    return errors;
+  }
+
+
+  /**
+   * Creates a default instance.
+   */
+  BaseDnRegistry()
+  {
+    this(new TreeMap<DN,Backend>(), new TreeMap<DN,Backend>(),
+         new TreeMap<DN,Backend>(), false);
+  }
+
+  /**
+   * Returns a copy of this registry.
+   *
+   * @return copy of this registry
+   */
+  BaseDnRegistry copy()
+  {
+    return new BaseDnRegistry(
+            new TreeMap<DN,Backend>(baseDNs),
+            new TreeMap<DN,Backend>(publicNamingContexts),
+            new TreeMap<DN,Backend>(privateNamingContexts),
+            true);
+  }
+
+
+  /**
+   * Creates a parameterized instance.
+   *
+   * @param baseDNs map
+   * @param publicNamingContexts map
+   * @param privateNamingContexts map
+   * @param testOnly indicates whether this registry will be used for testing;
+   *        when <code>true</code> this registry will not modify backends
+   */
+  private BaseDnRegistry(TreeMap<DN, Backend> baseDNs,
+                         TreeMap<DN, Backend> publicNamingContexts,
+                         TreeMap<DN, Backend> privateNamingContexts,
+                         boolean testOnly)
+  {
+    this.baseDNs = baseDNs;
+    this.publicNamingContexts = publicNamingContexts;
+    this.privateNamingContexts = privateNamingContexts;
+    this.testOnly = testOnly;
+  }
+
+
+  /**
+   * Gets the mapping of registered base DNs to their associated backend.
+   *
+   * @return mapping from base DN to backend
+   */
+  Map<DN,Backend> getBaseDnMap() {
+    return this.baseDNs;
+  }
+
+
+  /**
+   * Gets the mapping of registered public naming contexts to their
+   * associated backend.
+   *
+   * @return mapping from naming context to backend
+   */
+  Map<DN,Backend> getPublicNamingContextsMap() {
+    return this.publicNamingContexts;
+  }
+
+
+  /**
+   * Gets the mapping of registered private naming contexts to their
+   * associated backend.
+   *
+   * @return mapping from naming context to backend
+   */
+  Map<DN,Backend> getPrivateNamingContextsMap() {
+    return this.privateNamingContexts;
+  }
+
+
+
+
+  /**
+   * 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)
+  {
+    return (privateNamingContexts.containsKey(dn) ||
+            publicNamingContexts.containsKey(dn));
+  }
+
+
+  /**
+   * Clear and nullify this registry's internal state.
+   */
+  void clear() {
+
+    if (baseDNs != null)
+    {
+      baseDNs.clear();
+    }
+
+    if (privateNamingContexts != null)
+    {
+      privateNamingContexts.clear();
+    }
+
+    if (publicNamingContexts != null)
+    {
+      publicNamingContexts.clear();
+    }
+
+  }
+
+}
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index ab214a2..07e6df6 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -674,14 +674,10 @@
   // The thread group for all threads associated with the Directory Server.
   private ThreadGroup directoryThreadGroup;
 
-  // The set of base DNs registered with the server.
-  private TreeMap<DN,Backend> baseDNs;
 
-  // The set of private naming contexts registered with the server.
-  private TreeMap<DN,Backend> privateNamingContexts;
+  // Registry for base DN and naming context information.
+  private BaseDnRegistry baseDnRegistry;
 
-  // The set of public naming contexts registered with the server.
-  private TreeMap<DN,Backend> publicNamingContexts;
 
   // The set of backends registered with the server.
   private TreeMap<String,Backend> backends;
@@ -895,9 +891,7 @@
            new ConcurrentHashMap<String,Long>();
       directoryServer.backendInitializationListeners =
            new CopyOnWriteArraySet<BackendInitializationListener>();
-      directoryServer.baseDNs = new TreeMap<DN,Backend>();
-      directoryServer.publicNamingContexts = new TreeMap<DN,Backend>();
-      directoryServer.privateNamingContexts = new TreeMap<DN,Backend>();
+      directoryServer.baseDnRegistry = new BaseDnRegistry();
       directoryServer.changeNotificationListeners =
            new CopyOnWriteArrayList<ChangeNotificationListener>();
       directoryServer.persistentSearches =
@@ -6307,7 +6301,7 @@
    */
   public static Map<DN,Backend> getBaseDNs()
   {
-    return directoryServer.baseDNs;
+    return directoryServer.baseDnRegistry.getBaseDnMap();
   }
 
 
@@ -6323,7 +6317,7 @@
    */
   public static Backend getBackendWithBaseDN(DN baseDN)
   {
-    return directoryServer.baseDNs.get(baseDN);
+    return directoryServer.baseDnRegistry.getBaseDnMap().get(baseDN);
   }
 
 
@@ -6346,7 +6340,7 @@
       return directoryServer.rootDSEBackend;
     }
 
-    TreeMap<DN,Backend> baseDNs = directoryServer.baseDNs;
+    Map<DN,Backend> baseDNs = directoryServer.baseDnRegistry.getBaseDnMap();
     Backend b = baseDNs.get(entryDN);
     while (b == null)
     {
@@ -6363,6 +6357,20 @@
   }
 
 
+  /**
+   * Obtains a copy of the server's base DN registry.  The copy can be used
+   * to test registration/deregistration of base DNs but cannot be used to
+   * modify the backends.  To modify the server's live base DN to backend
+   * mappings use {@link #registerBaseDN(DN, Backend, boolean)} and
+   * {@link #deregisterBaseDN(DN)}.
+   *
+   * @return copy of the base DN regsitry
+   */
+  public static BaseDnRegistry copyBaseDnRegistry()
+  {
+    return directoryServer.baseDnRegistry.copy();
+  }
+
 
   /**
    * Registers the provided base DN with the server.
@@ -6375,209 +6383,37 @@
    *                    private base DN.  If the provided base DN is a naming
    *                    context, then this controls whether it is public or
    *                    private.
-   * @param  testOnly   Indicates whether to only test whether the new base DN
-   *                    registration would be successful without actually
-   *                    applying any changes.
    *
    * @throws  DirectoryException  If a problem occurs while attempting to
    *                              register the provided base DN.
    */
   public static void registerBaseDN(DN baseDN, Backend backend,
-                                    boolean isPrivate, boolean testOnly)
+                                    boolean isPrivate)
          throws DirectoryException
   {
     ensureNotNull(baseDN, backend);
 
     synchronized (directoryServer)
     {
-      TreeMap<DN,Backend> newBaseDNs =
-           new TreeMap<DN,Backend>(directoryServer.baseDNs);
-      TreeMap<DN,Backend> newPublicNamingContexts =
-           new TreeMap<DN,Backend>(directoryServer.publicNamingContexts);
-      TreeMap<DN,Backend> newPrivateNamingContexts =
-           new TreeMap<DN,Backend>(directoryServer.privateNamingContexts);
+      List<Message> warnings =
+              directoryServer.baseDnRegistry.registerBaseDN(
+                      baseDN, backend, isPrivate);
 
-
-      // Check to see if the base DN is already registered with the server.
-      Backend existingBackend = newBaseDNs.get(baseDN);
-      if (existingBackend != null)
-      {
-        Message message = ERR_REGISTER_BASEDN_ALREADY_EXISTS.
-            get(String.valueOf(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
-      // relationship with any other base Dns for the same backend.
-      LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
-      for (DN dn : newBaseDNs.keySet())
-      {
-        Backend b = newBaseDNs.get(dn);
-        if (b.equals(backend))
-        {
-          otherBaseDNs.add(dn);
-
-          if (baseDN.isAncestorOf(dn) || baseDN.isDescendantOf(dn))
-          {
-            Message message = ERR_REGISTER_BASEDN_HIERARCHY_CONFLICT.
-                get(String.valueOf(baseDN), backend.getBackendID(),
-                    String.valueOf(dn));
-            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
-                                         message);
-          }
+      // Since we've committed the changes we need to log any issues
+      // that this registration has caused
+      if (warnings != null) {
+        for (Message warning : warnings) {
+          logError(warning);
         }
       }
 
-
-      // Check to see if the new base DN is subordinate to any other base DN
-      // already defined.  If it is, then any other base DN(s) for the same
-      // backend must also be subordinate to the same base DN.
-      Backend superiorBackend = null;
-      DN      superiorBaseDN  = null;
-      DN      parentDN        = baseDN.getParent();
-      while (parentDN != null)
+      // Now create a workflow for the registered baseDN and register
+      // the workflow with the network groups, but don't register the
+      // workflow if the backend happens to be the configuration backend
+      // because it's too soon.
+      if (! baseDN.equals(DN.decode("cn=config")))
       {
-        if (newBaseDNs.containsKey(parentDN))
-        {
-          superiorBaseDN  = parentDN;
-          superiorBackend = newBaseDNs.get(parentDN);
-
-          for (DN dn : otherBaseDNs)
-          {
-            if (! dn.isDescendantOf(superiorBaseDN))
-            {
-              Message message = ERR_REGISTER_BASEDN_DIFFERENT_PARENT_BASES.
-                  get(String.valueOf(baseDN), backend.getBackendID(),
-                      String.valueOf(dn));
-              throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
-                                           message);
-            }
-          }
-
-          break;
-        }
-
-        parentDN = parentDN.getParent();
-      }
-
-      if (superiorBackend == null)
-      {
-        if (backend.getParentBackend() != null)
-        {
-          Message message = ERR_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE.
-              get(String.valueOf(baseDN), backend.getBackendID(),
-                  backend.getParentBackend().getBackendID());
-          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
-                                       message);
-        }
-      }
-
-
-      // Check to see if the new base DN should be the superior base DN for any
-      // other base DN(s) already defined.
-      LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
-      LinkedList<DN>      subordinateBaseDNs  = new LinkedList<DN>();
-      for (DN dn : newBaseDNs.keySet())
-      {
-        Backend b = newBaseDNs.get(dn);
-        parentDN = dn.getParent();
-        while (parentDN != null)
-        {
-          if (parentDN.equals(baseDN))
-          {
-            subordinateBaseDNs.add(dn);
-            subordinateBackends.add(b);
-            break;
-          }
-          else if (newBaseDNs.containsKey(parentDN))
-          {
-            break;
-          }
-
-          parentDN = parentDN.getParent();
-        }
-      }
-
-
-      // If we've gotten here, then the new base DN is acceptable.  If we should
-      // actually apply the changes then do so now.
-      if (! testOnly)
-      {
-        // Check to see if any of the registered backends already contain an
-        // entry with the DN specified as the base DN.  This could happen if
-        // we're creating a new subordinate backend in an existing directory
-        // (e.g., moving the "ou=People,dc=example,dc=com" branch to its own
-        // backend when that data already exists under the "dc=example,dc=com"
-        // backend).  This condition shouldn't prevent the new base DN from
-        // being registered, but it's definitely important enough that we let
-        // the administrator know about it and remind them that the existing
-        // backend will need to be reinitialized.
-        if (superiorBackend != null)
-        {
-          if (superiorBackend.entryExists(baseDN))
-          {
-            Message message = WARN_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS.
-                get(superiorBackend.getBackendID(), String.valueOf(baseDN),
-                    backend.getBackendID());
-            logError(message);
-          }
-        }
-
-
-        newBaseDNs.put(baseDN, backend);
-
-        if (superiorBackend == null)
-        {
-          if (isPrivate)
-          {
-            backend.setPrivateBackend(true);
-            newPrivateNamingContexts.put(baseDN, backend);
-          }
-          else
-          {
-            backend.setPrivateBackend(false);
-            newPublicNamingContexts.put(baseDN, backend);
-          }
-        }
-        else if (otherBaseDNs.isEmpty())
-        {
-          backend.setParentBackend(superiorBackend);
-          superiorBackend.addSubordinateBackend(backend);
-        }
-
-        for (Backend b : subordinateBackends)
-        {
-          Backend oldParentBackend = b.getParentBackend();
-          if (oldParentBackend != null)
-          {
-            oldParentBackend.removeSubordinateBackend(b);
-          }
-
-          b.setParentBackend(backend);
-          backend.addSubordinateBackend(b);
-        }
-
-        for (DN dn : subordinateBaseDNs)
-        {
-          newPublicNamingContexts.remove(dn);
-          newPrivateNamingContexts.remove(dn);
-        }
-
-        directoryServer.baseDNs               = newBaseDNs;
-        directoryServer.publicNamingContexts  = newPublicNamingContexts;
-        directoryServer.privateNamingContexts = newPrivateNamingContexts;
-
-        // Now create a workflow for the registered baseDN and register
-        // the workflow with the network groups, but don't register the
-        // workflow if the backend happens to be the configuration backend
-        // because it's too soon.
-        if (! baseDN.equals(DN.decode("cn=config")))
-        {
-          createAndRegisterWorkflow(baseDN, backend);
-        }
+        createAndRegisterWorkflow(baseDN, backend);
       }
     }
   }
@@ -6589,145 +6425,30 @@
    *
    * @param  baseDN     The base DN to deregister with the server.  It must not
    *                    be {@code null}.
-   * @param  testOnly   Indicates whether to only test whether the new base DN
-   *                    registration would be successful without actually
-   *                    applying any changes.
    *
    * @throws  DirectoryException  If a problem occurs while attempting to
    *                              deregister the provided base DN.
    */
-  public static void deregisterBaseDN(DN baseDN, boolean testOnly)
+  public static void deregisterBaseDN(DN baseDN)
          throws DirectoryException
   {
     ensureNotNull(baseDN);
 
-    synchronized (directoryServer)
-    {
-      TreeMap<DN,Backend> newBaseDNs =
-           new TreeMap<DN,Backend>(directoryServer.baseDNs);
-      TreeMap<DN,Backend> newPublicNamingContexts =
-           new TreeMap<DN,Backend>(directoryServer.publicNamingContexts);
-      TreeMap<DN,Backend> newPrivateNamingContexts =
-           new TreeMap<DN,Backend>(directoryServer.privateNamingContexts);
+    synchronized(directoryServer) {
 
+      List<Message> warnings =
+              directoryServer.baseDnRegistry.deregisterBaseDN(baseDN);
 
-      // Make sure that the Directory Server actually contains a backend with
-      // the specified base DN.
-      Backend backend = newBaseDNs.get(baseDN);
-      if (backend == null)
-      {
-        Message message =
-            ERR_DEREGISTER_BASEDN_NOT_REGISTERED.get(String.valueOf(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.
-      Backend             superiorBackend     = backend.getParentBackend();
-      LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
-      if (backend.getSubordinateBackends() != null)
-      {
-        for (Backend b : backend.getSubordinateBackends())
-        {
-          for (DN dn : b.getBaseDNs())
-          {
-            if (dn.isDescendantOf(baseDN))
-            {
-              subordinateBackends.add(b);
-              break;
-            }
-          }
+      // Since we've committed the changes we need to log any issues
+      // that this registration has caused
+      if (warnings != null) {
+        for (Message error : warnings) {
+          logError(error);
         }
       }
 
-
-      // See if there are any other base DNs registered within the same backend.
-      LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
-      for (DN dn : newBaseDNs.keySet())
-      {
-        if (dn.equals(baseDN))
-        {
-          continue;
-        }
-
-        Backend b = newBaseDNs.get(dn);
-        if (backend.equals(b))
-        {
-          otherBaseDNs.add(dn);
-        }
-      }
-
-
-      // If we've gotten here, then it's OK to make the changes.
-      if (! testOnly)
-      {
-        // Get rid of the references to this base DN in the mapping tree
-        // information.
-        newBaseDNs.remove(baseDN);
-        newPublicNamingContexts.remove(baseDN);
-        newPrivateNamingContexts.remove(baseDN);
-
-
-        if (superiorBackend == null)
-        {
-          // If there were any subordinate backends, then all of their base DNs
-          // will now be promoted to naming contexts.
-          for (Backend b : subordinateBackends)
-          {
-            b.setParentBackend(null);
-            backend.removeSubordinateBackend(b);
-
-            for (DN dn : b.getBaseDNs())
-            {
-              if (b.isPrivateBackend())
-              {
-                newPrivateNamingContexts.put(dn, b);
-              }
-              else
-              {
-                newPublicNamingContexts.put(dn, b);
-              }
-            }
-          }
-        }
-        else
-        {
-          // If there are no other base DNs for the associated backend, then
-          // remove this backend as a subordinate of the parent backend.
-          if (otherBaseDNs.isEmpty())
-          {
-            superiorBackend.removeSubordinateBackend(backend);
-          }
-
-
-          // If there are any subordinate backends, then they need to be made
-          // subordinate to the parent backend.  Also, we should log a warning
-          // message indicating that there may be inconsistent search results
-          // because some of the structural entries will be missing.
-          if (! subordinateBackends.isEmpty())
-          {
-            Message message = WARN_DEREGISTER_BASEDN_MISSING_HIERARCHY.get(
-                String.valueOf(baseDN), backend.getBackendID());
-            logError(message);
-
-            for (Backend b : subordinateBackends)
-            {
-              backend.removeSubordinateBackend(b);
-              superiorBackend.addSubordinateBackend(b);
-              b.setParentBackend(superiorBackend);
-            }
-          }
-        }
-
-
-        directoryServer.baseDNs               = newBaseDNs;
-        directoryServer.publicNamingContexts  = newPublicNamingContexts;
-        directoryServer.privateNamingContexts = newPrivateNamingContexts;
-
-        // Now deregister the workflow that was associated with the base DN.
-        deregisterWorkflow(baseDN);
-      }
+      // Now deregister the workflow that was associated with the base DN.
+      deregisterWorkflow(baseDN);
     }
   }
 
@@ -6741,7 +6462,7 @@
    */
   public static Map<DN,Backend> getPublicNamingContexts()
   {
-    return directoryServer.publicNamingContexts;
+    return directoryServer.baseDnRegistry.getPublicNamingContextsMap();
   }
 
 
@@ -6755,7 +6476,7 @@
    */
   public static Map<DN,Backend> getPrivateNamingContexts()
   {
-    return directoryServer.privateNamingContexts;
+    return directoryServer.baseDnRegistry.getPrivateNamingContextsMap();
   }
 
 
@@ -6771,8 +6492,7 @@
    */
   public static boolean isNamingContext(DN dn)
   {
-    return (directoryServer.publicNamingContexts.containsKey(dn) ||
-            directoryServer.privateNamingContexts.containsKey(dn));
+    return directoryServer.baseDnRegistry.containsNamingContext(dn);
   }
 
 
@@ -8578,22 +8298,10 @@
     shutdownHook             = null;
     workQueue                = null;
 
-    if (privateNamingContexts != null)
+    if (baseDnRegistry != null)
     {
-      privateNamingContexts.clear();
-      privateNamingContexts = null;
-    }
-
-    if (publicNamingContexts != null)
-    {
-      publicNamingContexts.clear();
-      publicNamingContexts = null;
-    }
-
-    if (baseDNs != null)
-    {
-      baseDNs.clear();
-      baseDNs = null;
+      baseDnRegistry.clear();
+      baseDnRegistry = null;
     }
 
     if (backends != null)
diff --git a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 1a54df8..e1ddbf4 100644
--- a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -735,8 +735,7 @@
       // conflict with user backend identifiers.
       setBackendID("__config.ldif__");
 
-      DirectoryServer.registerBaseDN(configRootEntry.getDN(), this, true,
-                                     false);
+      DirectoryServer.registerBaseDN(configRootEntry.getDN(), this, true);
     }
     catch (Exception e)
     {
@@ -1026,7 +1025,7 @@
   {
     try
     {
-      DirectoryServer.deregisterBaseDN(configRootEntry.getDN(), false);
+      DirectoryServer.deregisterBaseDN(configRootEntry.getDN());
     }
     catch (Exception e)
     {
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
index 4b82e06..9f85cda 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationBackend.java
@@ -232,7 +232,7 @@
     {
       try
       {
-        DirectoryServer.registerBaseDN(dn, this, false, false);
+        DirectoryServer.registerBaseDN(dn, this, false);
       }
       catch (Exception e)
       {
@@ -269,7 +269,7 @@
     {
       try
       {
-        DirectoryServer.deregisterBaseDN(dn, false);
+        DirectoryServer.deregisterBaseDN(dn);
       }
       catch (Exception e)
       {
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
index d0e66a5..8a4d599 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
@@ -111,7 +111,7 @@
   public void testDeregisterNonExistentBaseDN()
          throws Exception
   {
-    DirectoryServer.deregisterBaseDN(DN.decode("o=unregistered"), false);
+    DirectoryServer.deregisterBaseDN(DN.decode("o=unregistered"));
   }
 
 

--
Gitblit v1.10.0