From eedbc609a3b3920a2c402c51f894939b114410f8 Mon Sep 17 00:00:00 2001
From: jdemendi <jdemendi@localhost>
Date: Thu, 20 Sep 2007 07:08:29 +0000
Subject: [PATCH] 

---
 opends/src/server/org/opends/server/core/DirectoryServer.java                                     |  134 ++++++++++------
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java            |  165 ++++++++++++++++++++
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java |  132 ++++++++++++++++
 opends/src/server/org/opends/server/backends/RootDSEBackend.java                                  |    7 
 opends/src/server/org/opends/server/extensions/ConfigFileHandler.java                             |    4 
 5 files changed, 387 insertions(+), 55 deletions(-)

diff --git a/opends/src/server/org/opends/server/backends/RootDSEBackend.java b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
index 0f5528d..aeef964 100644
--- a/opends/src/server/org/opends/server/backends/RootDSEBackend.java
+++ b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -289,10 +289,9 @@
     supportedFeatures = new HashSet<String>(0);
 
 
-    // Set the backend ID for this backend.  We don't have to worry about
-    // potential conflicts because this backend will never get registered with
-    // the Directory Server like other backends.
-    setBackendID("rootdse");
+    // Set the backend ID for this backend. The identifier needs to be
+    // specific enough to avoid conflict with user backend identifiers.
+    setBackendID("__root.dse__");
 
 
     // Register as a change listener.
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 1f448f2..ab214a2 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -2587,32 +2587,49 @@
 
 
   /**
-   * Deregister a workflow from a network group.
+   * Deregisters a set of workflows each of which is identified with
+   * a baseDN.
    *
    * In the first implementation, workflows are stored in the default network
-   * group.
+   * group only.
    *
-   * @param backend  the backend which is handled by the workflows to
-   *                 deregister
+   * @param baseDNs  the DNs of the workflows to deregister
    */
   private static void deregisterWorkflows(
-      Backend backend
+      DN[] baseDNs
+      )
+  {
+    for (DN baseDN: baseDNs)
+    {
+      deregisterWorkflow(baseDN);
+    }
+  }
+
+
+  /**
+   * Deregisters one workflow with the appropriate network group.
+   *
+   * In the first implementation, workflows are stored in the default network
+   * group only.
+   *
+   * @param baseDN  the DN of the workflow to deregister
+   */
+  private static void deregisterWorkflow(
+      DN baseDN
       )
   {
     // Get the default network group and deregister all the workflows
     // being configured for the backend (reminder: there is one worklfow
     // per base DN configured in the backend).
     NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
-    for (DN baseDN: backend.getBaseDNs())
-    {
-      defaultNetworkGroup.deregisterWorkflow (baseDN);
-    }
+    defaultNetworkGroup.deregisterWorkflow (baseDN);
   }
 
 
   /**
-   * Create a workflow for the a backend then register the workflow
-   * with the appropriate network group.
+   * Creates a set of workflows for a given backend. There are as many
+   * workflows as base DNs defined in the backend. Each workflow is
+   * registered with the appropriate network group.
    *
    * TODO implement the registration with the appropriate network group.
    *
@@ -2622,28 +2639,55 @@
    *                              workflow conflicts with the workflow
    *                              ID of an existing workflow.
    */
-  private static void createAndRegisterWorkflows(
+  public static void createAndRegisterWorkflows(
       Backend backend
       ) throws DirectoryException
   {
-    // Create a root workflow element to encapsulate the backend
-    LocalBackendWorkflowElement rootWE =
-        new LocalBackendWorkflowElement(backend.getBackendID(), backend);
-
     // Create a worklfow for each baseDN being configured
     // in the backend and register the workflow with the network groups.
-    // The workflow identifier is the same as the backend ID.
+    // In the automatic configuration mode, the workflow identifier is
+    // set to the backend ID.
     for (DN curBaseDN: backend.getBaseDNs())
     {
-      WorkflowImpl workflowImpl = new WorkflowImpl(
-          curBaseDN.toString(), curBaseDN, (WorkflowElement) rootWE);
-      registerWorkflowInNetworkGroups(workflowImpl);
+      createAndRegisterWorkflow(curBaseDN, backend);
     }
   }
 
 
   /**
-   * Register a workflow with the appropriate network groups.
+   * Creates one workflow for a given base DN in a backend. The workflow
+   * is registered with the appropriate network group.
+   *
+   * TODO implement the registration with the appropriate network group.
+   *
+   * @param baseDN   the base DN of the workflow to create
+   * @param backend  the backend handled by the workflow
+   *
+   * @throws  DirectoryException  If the workflow ID for the provided
+   *                              workflow conflicts with the workflow
+   *                              ID of an existing workflow.
+   */
+  public static void createAndRegisterWorkflow(
+      DN      baseDN,
+      Backend backend
+      ) throws DirectoryException
+  {
+    String backendID = backend.getBackendID();
+
+    // Create a root workflow element to encapsulate the backend
+    LocalBackendWorkflowElement rootWE =
+        LocalBackendWorkflowElement.create(backendID, backend);
+
+    // Create the worklfow for the base DN and register the workflow with
+    // the appropriate network groups.
+    WorkflowImpl workflowImpl = new WorkflowImpl(
+        baseDN.toString(), baseDN, (WorkflowElement) rootWE);
+    registerWorkflowInNetworkGroups(workflowImpl);
+  }
+
+
+  /**
+   * Registers a workflow with the appropriate network groups.
    *
    * In the first implementation, the workflow is registered with the
    * default network group only.
@@ -2661,7 +2705,6 @@
       WorkflowImpl workflowImpl
       ) throws DirectoryException
   {
-    // Register first the workflow with the default network group
     NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
     defaultNetworkGroup.registerWorkflow(workflowImpl);
 
@@ -2674,14 +2717,8 @@
 
 
   /**
-   * Create the workflows for the backends that were not registered through
-   * registerBackend method, nemely cn=config and RootDSE.
-   *
-   * TODO jdemendi - read the Workflow config and create them accordingly
-   *
-   * For the prototype: there is no configuration for the workflows.
-   * So we create one workflow per local backend, and we register it
-   * to the pool.
+   * Creates the workflows for the backends whose baseDNs were not registered
+   * with registerBaseDN method, namely config backend and RootDSE backend.
    *
    * @throws  ConfigException  If there is a configuration problem with any of
    *                           the workflows.
@@ -2691,10 +2728,7 @@
   {
     try
     {
-      // Create a workflow for the cn=config backend
       createAndRegisterWorkflows (configHandler);
-
-      // Create a workflows for the rootDSE backend
       createAndRegisterWorkflows (rootDSEBackend);
     }
     catch (DirectoryException de)
@@ -6190,15 +6224,6 @@
         monitor.initializeMonitorProvider(null);
         backend.setBackendMonitor(monitor);
         registerMonitorProvider(monitor);
-
-        // FIXME jdemendi - temporary code: create one workflow for each
-        // base DN being configured in the backend. We should not rely on the
-        // backend registration to create and register workflows because
-        // a workflow can be created for different type of "backend", including
-        // remote lDAP server. Instead, we should create the workflows using
-        // the administration framework. We will be doing so as soon as we
-        // have a configuration section for the workflows.
-        createAndRegisterWorkflows (backend);
       }
     }
   }
@@ -6225,12 +6250,9 @@
 
       directoryServer.backends = newBackends;
 
-      // Delete all the workflows registered for the backend.
-      // FIXME jdemendi - This task should be performed in the scope of the
-      // administration framework. However the administration framework
-      // requires a configuration section for the workflows which we don't have
-      // as of today.
-      deregisterWorkflows (backend);
+      // Don't need anymore the local backend workflow element
+      LocalBackendWorkflowElement.remove(backend.getBackendID());
+
 
       BackendMonitor monitor = backend.getBackendMonitor();
       if (monitor != null)
@@ -6547,6 +6569,15 @@
         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);
+        }
       }
     }
   }
@@ -6693,6 +6724,9 @@
         directoryServer.baseDNs               = newBaseDNs;
         directoryServer.publicNamingContexts  = newPublicNamingContexts;
         directoryServer.privateNamingContexts = newPrivateNamingContexts;
+
+        // Now deregister the workflow that was associated with the base DN.
+        deregisterWorkflow(baseDN);
       }
     }
   }
@@ -8396,6 +8430,10 @@
     {
       try
       {
+        // Deregister all the local backend workflow elements that have been
+        // registered with the server.
+        LocalBackendWorkflowElement.removeAll();
+
         for (BackendInitializationListener listener :
              directoryServer.backendInitializationListeners)
         {
diff --git a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 9cd1262..1a54df8 100644
--- a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -731,6 +731,10 @@
 
     try
     {
+      // Set a backend ID for the config backend. Try to avoid potential
+      // conflict with user backend identifiers.
+      setBackendID("__config.ldif__");
+
       DirectoryServer.registerBaseDN(configRootEntry.getDN(), this, true,
                                      false);
     }
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 255cd3f..c6f4b33 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -46,6 +46,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 import java.util.concurrent.locks.Lock;
 
 import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn;
@@ -139,8 +140,17 @@
    */
   private static final DebugTracer TRACER = DebugLogger.getTracer();
 
-  // the backend associated to that workflow element
-  Backend backend;
+  // the backend associated with the local workflow element
+  private Backend backend;
+
+  // the set of local backend workflow elements registered with the server
+  private static
+    TreeMap<String, LocalBackendWorkflowElement> registeredLocalBackends =
+      new TreeMap<String, LocalBackendWorkflowElement>();
+
+  // a lock to guarantee safe concurrent access to the registeredLocalBackends
+  // variable
+  private static Object registeredLocalBackendsLock = new Object();
 
 
   /**
@@ -149,7 +159,7 @@
    * @param workflowElementID  the workflow element identifier
    * @param backend  the backend associated to that workflow element
    */
-  public LocalBackendWorkflowElement(
+  private LocalBackendWorkflowElement(
       String  workflowElementID,
       Backend backend
       )
@@ -166,6 +176,122 @@
 
 
   /**
+   * Creates and registers a local backend with the server.
+   *
+   * @param workflowElementID  the identifier of the workflow element to create
+   * @param backend            the backend to associate with the local backend
+   *                           workflow element
+   *
+   * @return the existing local backend workflow element if it was
+   *         already created or a newly created local backend workflow
+   *         element.
+   */
+  public static LocalBackendWorkflowElement create(
+      String  workflowElementID,
+      Backend backend
+      )
+  {
+    LocalBackendWorkflowElement localBackend = null;
+
+    // If the requested workflow element does not exist then create one.
+    localBackend = registeredLocalBackends.get(workflowElementID);
+    if (localBackend == null)
+    {
+      localBackend = new LocalBackendWorkflowElement(
+          workflowElementID, backend);
+
+      // store the new local backend in the list of registered backends
+      registerLocalBackend(localBackend);
+    }
+
+    return localBackend;
+  }
+
+
+  /**
+   * Removes a local backend that was registered with the server.
+   *
+   * @param workflowElementID  the identifier of the workflow element to remove
+   */
+  public static void remove(
+      String workflowElementID
+      )
+  {
+    deregisterLocalBackend(workflowElementID);
+  }
+
+
+  /**
+   * Removes all the local backends that were registered with the server.
+   * This function is intended to be called when the server is shutting down.
+   */
+  public static void removeAll()
+  {
+    synchronized (registeredLocalBackendsLock)
+    {
+      for (LocalBackendWorkflowElement localBackend:
+           registeredLocalBackends.values())
+      {
+        deregisterLocalBackend(localBackend.getWorkflowElementID());
+      }
+    }
+  }
+
+
+  /**
+   * Registers a local backend with the server.
+   *
+   * @param localBackend  the local backend to register with the server
+   */
+  private static void registerLocalBackend(
+      LocalBackendWorkflowElement localBackend
+      )
+  {
+    synchronized (registeredLocalBackendsLock)
+    {
+      String localBackendID = localBackend.getWorkflowElementID();
+      LocalBackendWorkflowElement existingLocalBackend =
+        registeredLocalBackends.get(localBackendID);
+
+      if (existingLocalBackend == null)
+      {
+        TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
+          new TreeMap
+            <String, LocalBackendWorkflowElement>(registeredLocalBackends);
+        newLocalBackends.put(localBackendID, localBackend);
+        registeredLocalBackends = newLocalBackends;
+      }
+    }
+  }
+
+
+  /**
+   * Deregisters a local backend with the server.
+   *
+   * @param workflowElementID  the identifier of the workflow element to remove
+   */
+  private static void deregisterLocalBackend(
+      String workflowElementID
+      )
+  {
+    synchronized (registeredLocalBackendsLock)
+    {
+      LocalBackendWorkflowElement existingLocalBackend =
+        registeredLocalBackends.get(workflowElementID);
+
+      if (existingLocalBackend != null)
+      {
+        TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
+          new TreeMap
+            <String, LocalBackendWorkflowElement>(registeredLocalBackends);
+        newLocalBackends.remove(workflowElementID);
+        registeredLocalBackends = newLocalBackends;
+      }
+    }
+  }
+
+
+  /**
    * {@inheritDoc}
    */
   public void execute(Operation operation)
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
index c341d7f..67d8e1a 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
@@ -31,11 +31,22 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
+import static org.opends.server.config.ConfigConstants.DN_BACKEND_BASE;
+
+import java.util.ArrayList;
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.DirectoryServerTestCase;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.types.Attribute;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchScope;
 import org.opends.server.workflowelement.WorkflowElement;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -473,6 +484,160 @@
 
 
   /**
+   * This test checks that network groups are updated as appropriate when
+   * backend base DNs are added or removed. When a new backend base DN is
+   * added, the new suffix should be accessible for the route process - ie.
+   * a workflow should be created and be a potential candidate for the route
+   * process. Simillarly, when a backend base DN is removed its associated
+   * workflow should be removed; subsequently, any request targetting the
+   * removed suffix should be rejected and a no such entry status code be
+   * returned.
+   */
+  @Test
+  public void testBackendBaseDNModification()
+         throws Exception
+  {
+    String suffix  = "dc=example,dc=com";
+    String suffix2 = "o=networkgroup suffix";
+    String backendBaseDNName = "ds-cfg-backend-base-dn";
+
+    // Initialize a backend with a base entry.
+    TestCaseUtils.clearJEBackend(true, "userRoot", suffix);
+
+    // Create a client connection for the test.
+    InternalClientConnection connection =
+      InternalClientConnection.getRootConnection();
+
+    // Check that suffix is accessible while suffix2 is not.
+    searchEntry(connection, suffix,  true);
+    searchEntry(connection, suffix2, false);
+
+    // Add a new suffix in the backend and create a base entry for the
+    // new suffix.
+    String backendConfigDN = "ds-cfg-backend-id=userRoot," + DN_BACKEND_BASE;
+    modifyAttribute(
+        connection, backendConfigDN,
+        ModificationType.ADD, backendBaseDNName, suffix2);
+    addBaseEntry(connection, suffix2, "networkgroup suffix");
+
+    // Both old and new suffix should be accessible.
+    searchEntry(connection, suffix,  true);
+    searchEntry(connection, suffix2, true);
+
+    // Remove the new suffix...
+    modifyAttribute(
+        connection, backendConfigDN,
+        ModificationType.DELETE, backendBaseDNName, suffix2);
+
+    // ...and check that the removed suffix is no more accessible.
+    searchEntry(connection, suffix,  true);
+    searchEntry(connection, suffix2, false);
+    
+    // Replace the suffix with suffix2 in the backend
+    modifyAttribute(
+        connection, backendConfigDN,
+        ModificationType.REPLACE, backendBaseDNName, suffix2);
+
+    // Now none of the suffixes are accessible: this means the entries
+    // under the old suffix are not moved to the new suffix.
+    searchEntry(connection, suffix,  false);
+    searchEntry(connection, suffix2, false);
+    
+    // Add a base entry for the new suffix
+    addBaseEntry(connection, suffix2, "networkgroup suffix");
+    
+    // The new suffix is accessible while the old one is not.
+    searchEntry(connection, suffix,  false);
+    searchEntry(connection, suffix2, true);
+  }
+
+
+  /**
+   * Searches an entry on a given connection.
+   *
+   * @param connection    the connection to use for the search request
+   * @param baseDN        the request base DN string
+   * @param shouldExist   if true the searched entry is expected to be found
+   */
+  private void searchEntry(
+      InternalClientConnection connection,
+      String  baseDN,
+      boolean shouldExist
+      ) throws Exception
+  {
+    SearchOperation search = connection.processSearch(
+        DN.decode(baseDN),
+        SearchScope.BASE_OBJECT,
+        LDAPFilter.decode("(objectClass=*)").toSearchFilter());
+
+    // Compare the result code with the expected one
+    ResultCode resultCode = search.getResultCode();
+    if (shouldExist)
+    {
+      assertEquals(resultCode, ResultCode.SUCCESS);
+    }
+    else
+    {
+      assertEquals(resultCode, ResultCode.NO_SUCH_OBJECT);
+    }
+  }
+
+
+  /**
+   * Creates a base entry for the given suffix.
+   *
+   * @param connection  the connection to use for the add request
+   * @param suffix      the suffix for which the base entry is to be created
+   */
+  private void addBaseEntry(
+      InternalClientConnection connection,
+      String  suffix,
+      String  namingAttribute
+      ) throws Exception
+  {
+    Entry e = TestCaseUtils.makeEntry(
+        "dn: " + suffix,
+        "objectClass: top",
+        "objectClass: organization",
+        "o: " + namingAttribute);
+
+   AddOperation addOperation = connection.processAdd(
+       e.getDN(),
+       e.getObjectClasses(),
+       e.getUserAttributes(),
+       e.getOperationalAttributes());
+
+   assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+  /**
+   * Adds/Deletes/Replaces an attribute in a given entry.
+   *
+   * @param connection      the connection to use for the modify request
+   * @param baseDN          the request base DN string
+   * @param modType         the modification type (add/delete/replace)
+   * @param attributeName   the name  of the attribute to add/delete/replace
+   * @param attributeValue  the value of the attribute to add/delete/replace
+   */
+  private void modifyAttribute(
+      InternalClientConnection connection,
+      String  baseDN,
+      ModificationType modType,
+      String  attributeName,
+      String  attributeValue
+      ) throws Exception
+  {
+    ArrayList<Modification> mods = new ArrayList<Modification>();
+    Attribute attributeToModify = new Attribute(attributeName, attributeValue);
+    mods.add(new Modification(modType, attributeToModify));
+    ModifyOperation modifyOperation = connection.processModify(
+        DN.decode(baseDN), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+  /**
    * Checks the DN routing through a network group.
    *
    * @param networkGroup    the network group to use for the check

--
Gitblit v1.10.0