From 8bf57178be13de90174a200ca75e9df959d0ef25 Mon Sep 17 00:00:00 2001
From: jdemendi <jdemendi@localhost>
Date: Wed, 08 Aug 2007 09:57:42 +0000
Subject: [PATCH] This set of changes add ID to identify network groups, workflows and workflow elements. These identifiers pave the way for the network group and workflow configuration.

---
 opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java                                     |   39 +
 opendj-sdk/opends/src/server/org/opends/server/core/NetworkGroup.java                                        |  361 ++++++++++++++----
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java                      |   11 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java            |  446 +++++++++++++++--------
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java        |   77 +++
 opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java                                    |   43 ++
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java |    4 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java                          |   22 +
 opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java                                        |  106 +++++
 9 files changed, 829 insertions(+), 280 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index f315d7d..be769b3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -2427,21 +2427,26 @@
    * TODO implement the registration with the appropriate network group.
    *
    * @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.
    */
   private static void createAndRegisterWorkflows(
       Backend backend
-      )
+      ) throws DirectoryException
   {
     // Create a root workflow element to encapsulate the backend
     LocalBackendWorkflowElement rootWE =
-        new LocalBackendWorkflowElement(backend);
+        new LocalBackendWorkflowElement(backend.getBackendID(), backend);
 
     // Create a worklfow for each baseDN being configured
-    // in the backend and register the workflow with the network groups
+    // in the backend and register the workflow with the network groups.
+    // The workflow identifier is the same as the backend ID.
     for (DN curBaseDN: backend.getBaseDNs())
     {
       WorkflowImpl workflowImpl = new WorkflowImpl(
-          curBaseDN, (WorkflowElement) rootWE);
+          curBaseDN.toString(), curBaseDN, (WorkflowElement) rootWE);
       registerWorkflowInNetworkGroups(workflowImpl);
     }
   }
@@ -2456,10 +2461,15 @@
    * TODO implement the registration with the appropriate network group.
    *
    * @param workflowImpl  the workflow to register
+   *
+   * @throws  DirectoryException  If the workflow ID for the provided
+   *                              workflow conflicts with the workflow
+   *                              ID of an existing workflow in a
+   *                              network group.
    */
   private static void registerWorkflowInNetworkGroups(
       WorkflowImpl workflowImpl
-      )
+      ) throws DirectoryException
   {
     // Register first the workflow with the default network group
     NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
@@ -2482,14 +2492,25 @@
    * 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.
+   *
+   * @throws  ConfigException  If there is a configuration problem with any of
+   *                           the workflows.
    */
   private void createAndRegisterRemainingWorkflows()
+      throws ConfigException
   {
-    // Create a workflow for the cn=config backend
-    createAndRegisterWorkflows (configHandler);
+    try
+    {
+      // Create a workflow for the cn=config backend
+      createAndRegisterWorkflows (configHandler);
 
-    // Create a workflows for the rootDSE backend
-    createAndRegisterWorkflows (rootDSEBackend);
+      // Create a workflows for the rootDSE backend
+      createAndRegisterWorkflows (rootDSEBackend);
+    }
+    catch (DirectoryException de)
+    {
+      throw new ConfigException(de.getMessageID(), de.getErrorMessage());
+    }
   }
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/NetworkGroup.java b/opendj-sdk/opends/src/server/org/opends/server/core/NetworkGroup.java
index 516e24d..a510884 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/NetworkGroup.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/NetworkGroup.java
@@ -27,31 +27,42 @@
 package org.opends.server.core;
 
 
-import java.util.ArrayList;
+import static org.opends.server.messages.CoreMessages.*;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.TreeMap;
 
 import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.ResultCode;
 import org.opends.server.workflowelement.WorkflowElement;
 
 
 /**
  * This class defines the network group. A network group is used to categorize
  * client connections. A network group is defined by a set of criteria, a
- * set of policies and a set of workflows. A client connection belongs to a
- * network group whenever it satisfies all the network group criteria. As soon
- * as a client connection belongs to a network group, it has to comply with
- * all the network group policies and all the client operation can be routed
- * to one the network group workflows.
+ * set of policies and a set of workflow nodes. A client connection belongs to
+ * a network group whenever it satisfies all the network group criteria. As
+ * soon as a client connection belongs to a network group, it has to comply
+ * with all the network group policies. Any cleared client operation can be
+ * routed to one the network group workflow nodes.
  */
 public class NetworkGroup
 {
-  // Workflows registered with the current network group.
-  private ArrayList<WorkflowTopologyNode> registeredWorkflows =
-      new ArrayList<WorkflowTopologyNode>();
+  // Workflow nodes registered with the current network group.
+  // Keys are workflowIDs.
+  private TreeMap<String, WorkflowTopologyNode> registeredWorkflowNodes =
+      new TreeMap<String, WorkflowTopologyNode>();
 
 
-  // The workflow for the rootDSE entry. The RootDSE workflow
-  // is not stored in the list of registered workflows.
-  private RootDseWorkflowTopology rootDSEWorkflow = null;
+  // A lock to protect concurrent access to the registered Workflow nodes.
+  private static Object registeredWorkflowNodesLock = new Object();
+
+
+  // The workflow node for the rootDSE entry. The RootDSE workflow node
+  // is not stored in the list of registered workflow nodes.
+  private RootDseWorkflowTopology rootDSEWorkflowNode = null;
 
 
   // List of naming contexts handled by the network group.
@@ -68,39 +79,77 @@
       new NetworkGroup ("default");
 
 
-  // The list of all network groups that have been created.
-  // The default network group is not stored in that pool.
-  private static ArrayList<NetworkGroup> networkGroupPool =
-      new ArrayList<NetworkGroup>();
+  // The list of all network groups that are registered with the server.
+  // The defaultNetworkGroup is not in the list of registered network groups.
+  private static TreeMap<String, NetworkGroup> registeredNetworkGroups =
+      new TreeMap<String, NetworkGroup>();
 
 
-  // Human readable network group name.
-  private String networkGroupName = null;
+  // A lock to protect concurrent access to the registeredNetworkGroups.
+  private static Object registeredNetworkGroupsLock = new Object();
+
+
+  // The network group internal identifier.
+  private String networkGroupID = null;
+
 
 
   /**
    * Creates a new instance of the network group.
    *
-   * @param networkGroupName  the name of the network group for debug purpose
+   * @param networkGroupID  the network group internal identifier
    */
-  public NetworkGroup (
-      String networkGroupName
+  public NetworkGroup(
+      String networkGroupID
       )
   {
-    this.networkGroupName = networkGroupName;
+    this.networkGroupID = networkGroupID;
   }
 
 
   /**
-   * Registers a network group with the pool of network groups.
+   * Registers the current network group (this) with the server.
    *
-   * @param networkGroup  the network group to register
+   * @throws  DirectoryException  If the network group ID for the provided
+   *                              network group conflicts with the network
+   *                              group ID of an existing network group.
    */
-  public static void registerNetworkGroup(
-      NetworkGroup networkGroup
-      )
+  public void register()
+      throws DirectoryException
   {
-    networkGroupPool.add(networkGroup);
+    ensureNotNull(networkGroupID);
+
+    synchronized (registeredNetworkGroupsLock)
+    {
+      // The network group must not be already registered
+      if (registeredNetworkGroups.containsKey(networkGroupID))
+      {
+        int msgID = MSGID_REGISTER_NETWORK_GROUP_ALREADY_EXISTS;
+        String message = getMessage(msgID, networkGroupID);
+        throw new DirectoryException(
+            ResultCode.UNWILLING_TO_PERFORM, message, msgID);
+      }
+
+      TreeMap<String, NetworkGroup> newRegisteredNetworkGroups =
+        new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
+      newRegisteredNetworkGroups.put(networkGroupID, this);
+      registeredNetworkGroups = newRegisteredNetworkGroups;
+    }
+  }
+
+
+  /**
+   * Deregisters the current network group (this) with the server.
+   */
+  public void deregister()
+  {
+    synchronized (registeredNetworkGroupsLock)
+    {
+      TreeMap<String, NetworkGroup> networkGroups =
+        new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
+      networkGroups.remove(networkGroupID);
+      registeredNetworkGroups = networkGroups;
+    }
   }
 
 
@@ -108,10 +157,14 @@
    * Registers a workflow with the network group.
    *
    * @param workflow  the workflow to register
+   *
+   * @throws  DirectoryException  If the workflow ID for the provided
+   *                              workflow conflicts with the workflow
+   *                              ID of an existing workflow.
    */
   public void registerWorkflow(
       WorkflowImpl workflow
-      )
+      ) throws DirectoryException
   {
     // The workflow is rgistered with no pre/post workflow element.
     registerWorkflow(workflow, null, null);
@@ -125,12 +178,16 @@
    * @param workflow              the workflow to register
    * @param preWorkflowElements   the tasks to execute before the workflow
    * @param postWorkflowElements  the tasks to execute after the workflow
+   *
+   * @throws  DirectoryException  If the workflow ID for the provided
+   *                              workflow conflicts with the workflow
+   *                              ID of an existing workflow.
    */
   private void registerWorkflow(
       WorkflowImpl workflow,
       WorkflowElement[] preWorkflowElements,
       WorkflowElement[] postWorkflowElements
-      )
+      ) throws DirectoryException
   {
     // true as soon as the workflow has been registered
     boolean registered = false;
@@ -139,45 +196,44 @@
     DN baseDN = workflow.getBaseDN();
     if (baseDN.isNullDN())
     {
-      // NOTE - The rootDSE workflow is not stored in the pool.
-      rootDSEWorkflow = new RootDseWorkflowTopology(workflow, namingContexts);
+      // NOTE - The rootDSE workflow is stored with the registeredWorkflows.
+      rootDSEWorkflowNode =
+        new RootDseWorkflowTopology(workflow, namingContexts);
       registered = true;
     }
     else
     {
       // This workflow is not the rootDSE workflow. Try to insert it in the
       // workflow topology.
-      if (! baseDNAlreadyRegistered(baseDN))
+      WorkflowTopologyNode workflowNode = new WorkflowTopologyNode(
+          workflow, preWorkflowElements, postWorkflowElements);
+
+      // Register the workflow node with the network group. If the workflow
+      // ID is already existing then an exception is raised.
+      registerWorkflowNode(workflowNode);
+      registered = true;
+
+      // Now add the workflow in the workflow topology...
+      for (WorkflowTopologyNode curNode: registeredWorkflowNodes.values())
       {
-        WorkflowTopologyNode workflowTopology = new WorkflowTopologyNode(
-            workflow, preWorkflowElements, postWorkflowElements);
-
-        // Add the workflow in the workflow topology...
-        for (WorkflowTopologyNode curWorkflow: registeredWorkflows)
+        // Try to insert the new workflow under an existing workflow...
+        if (curNode.insertSubordinate(workflowNode))
         {
-          // Try to insert the new workflow under an existing workflow...
-          if (curWorkflow.insertSubordinate(workflowTopology))
-          {
-            // new workflow has been inserted in the topology
-            break;
-          }
-
-          // ... or try to insert the existing workflow below the new
-          // workflow
-          if (workflowTopology.insertSubordinate(curWorkflow))
-          {
-            // new workflow has been inserted in the topology
-            break;
-          }
+          // new workflow has been inserted in the topology
+          break;
         }
 
-        // ... then register the workflow with the pool.
-        registeredWorkflows.add(workflowTopology);
-        registered = true;
-
-        // Rebuild the list of naming context handled by the network group
-        rebuildNamingContextList();
+        // ... or try to insert the existing workflow below the new
+        // workflow
+        if (workflowNode.insertSubordinate(curNode))
+        {
+          // new workflow has been inserted in the topology
+          break;
+        }
       }
+
+      // Rebuild the list of naming context handled by the network group
+      rebuildNamingContextList();
     }
 
     // If the workflow has been registered successfully then register it
@@ -194,26 +250,98 @@
 
 
   /**
-   * Deregisters a workflow with the network group.
+   * Deregisters a workflow with the network group. The workflow to
+   * deregister is identified by its baseDN.
    *
-   * @param baseDN  the baseDN of the workflow to deregister
+   * @param baseDN  the baseDN of the workflow to deregister, may be null
    */
-  public void deregisterWorkflow (
+  public void deregisterWorkflow(
       DN baseDN
       )
   {
-    Workflow workflow = getWorkflowCandidate(baseDN);
-    if (workflow != null)
+    if (baseDN == null)
     {
-      deregisterWorkflow(workflow);
+      return;
+    }
+
+    if (baseDN.isNullDN())
+    {
+      // deregister the rootDSE
+      deregisterWorkflow(rootDSEWorkflowNode);
+    }
+    else
+    {
+      // deregister a workflow node
+      synchronized (registeredWorkflowNodesLock)
+      {
+        for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
+        {
+          DN curDN = node.getBaseDN();
+          if (curDN.equals(baseDN))
+          {
+            // Call deregisterWorkflow() instead of deregisterWorkflowNode()
+            // because we want the naming context list to be updated as well.
+            deregisterWorkflow(node);
+
+            // Only one workflow can match the baseDN, so we can break
+            // the loop here.
+            break;
+          }
+        }
+      }
     }
   }
 
 
   /**
-   * Deregisters a workflow with the network group.
+   * Deregisters a workflow with the network group. The workflow to
+   * deregister is identified by its workflow ID.
    *
-   * @param workflow  the workflow to deregister
+   * @param workflowID the workflow identifier of the workflow to deregister
+   */
+  public void deregisterWorkflow(
+      String workflowID
+      )
+  {
+    String rootDSEWorkflowID = null;
+    if (rootDSEWorkflowNode != null)
+    {
+      rootDSEWorkflowID = rootDSEWorkflowNode.getWorkflowImpl().getWorkflowId();
+    }
+
+    if (workflowID.equalsIgnoreCase(rootDSEWorkflowID))
+    {
+      // deregister the rootDSE
+      deregisterWorkflow(rootDSEWorkflowNode);
+    }
+    else
+    {
+      // deregister a workflow node
+      synchronized (registeredWorkflowNodesLock)
+      {
+        for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
+        {
+          String curID = node.getWorkflowImpl().getWorkflowId();
+          if (curID.equals(workflowID))
+          {
+            // Call deregisterWorkflow() instead of deregisterWorkflowNode()
+            // because we want the naming context list to be updated as well.
+            deregisterWorkflow(node);
+
+            // Only one workflow can match the baseDN, so we can break
+            // the loop here.
+            break;
+          }
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Deregisters a workflow node with the network group.
+   *
+   * @param workflowNode  the workflow node to deregister
    */
   private void deregisterWorkflow(
       Workflow workflow
@@ -223,21 +351,21 @@
     boolean deregistered = false;
 
     // Is it the rootDSE workflow?
-    if (workflow == rootDSEWorkflow)
+    if (workflow == rootDSEWorkflowNode)
     {
-      rootDSEWorkflow = null;
+      rootDSEWorkflowNode = null;
       deregistered = true;
     }
     else
     {
+      // Deregister the workflow with the network group.
+      WorkflowTopologyNode workflowNode = (WorkflowTopologyNode) workflow;
+      deregisterWorkflowNode(workflowNode);
+      deregistered = true;
+
       // The workflow to deregister is not the root DSE workflow.
       // Remove it from the workflow topology.
-      WorkflowTopologyNode workflowTopology = (WorkflowTopologyNode) workflow;
-      workflowTopology.remove();
-
-      // Then deregister the workflow with the network group.
-      registeredWorkflows.remove(workflow);
-      deregistered = true;
+      workflowNode.remove();
 
       // Rebuild the list of naming context handled by the network group
       rebuildNamingContextList();
@@ -245,12 +373,63 @@
 
     // If the workflow has been deregistered then deregister it with
     // the default network group as well
-    if (deregistered)
+    if (deregistered && (this != defaultNetworkGroup))
     {
-      if (this != defaultNetworkGroup)
+      defaultNetworkGroup.deregisterWorkflow(workflow);
+    }
+  }
+
+
+  /**
+   * Registers a workflow node with the network group.
+   *
+   * @param workflowNode  the workflow node to register
+   *
+   * @throws  DirectoryException  If the workflow node ID for the provided
+   *                              workflow node conflicts with the workflow
+   *                              node ID of an existing workflow node.
+   */
+  private void registerWorkflowNode(
+      WorkflowTopologyNode workflowNode
+      ) throws DirectoryException
+  {
+    String workflowID = workflowNode.getWorkflowImpl().getWorkflowId();
+    ensureNotNull(workflowID);
+
+    synchronized (registeredWorkflowNodesLock)
+    {
+      // The workflow must not be already registered
+      if (registeredWorkflowNodes.containsKey(workflowID))
       {
-        defaultNetworkGroup.deregisterWorkflow(workflow);
+        int msgID = MSGID_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS;
+        String message = getMessage(msgID, workflowID, networkGroupID);
+        throw new DirectoryException(
+            ResultCode.UNWILLING_TO_PERFORM, message, msgID);
       }
+
+      TreeMap<String, WorkflowTopologyNode> newRegisteredWorkflowNodes =
+        new TreeMap<String, WorkflowTopologyNode>(registeredWorkflowNodes);
+      newRegisteredWorkflowNodes.put(workflowID, workflowNode);
+      registeredWorkflowNodes = newRegisteredWorkflowNodes;
+    }
+  }
+
+
+  /**
+   * Deregisters the current worklow (this) with the server.
+   *
+   * @param workflowNode  the workflow node to deregister
+   */
+  private void deregisterWorkflowNode(
+      WorkflowTopologyNode workflowNode
+      )
+  {
+    synchronized (registeredWorkflowNodesLock)
+    {
+      TreeMap<String, WorkflowTopologyNode> newWorkflowNodes =
+        new TreeMap<String, WorkflowTopologyNode>(registeredWorkflowNodes);
+      newWorkflowNodes.remove(workflowNode.getWorkflowImpl().getWorkflowId());
+      registeredWorkflowNodes = newWorkflowNodes;
     }
   }
 
@@ -262,7 +441,7 @@
    * @return the highest workflow in the topology that can handle the base DN,
    *         <code>null</code> if none was found
    */
-  public Workflow getWorkflowCandidate (
+  public Workflow getWorkflowCandidate(
       DN baseDN
       )
   {
@@ -273,7 +452,7 @@
     if (baseDN.isNullDN())
     {
       // The rootDSE workflow is the candidate.
-      workflowCandidate = rootDSEWorkflow;
+      workflowCandidate = rootDSEWorkflowNode;
     }
     else
     {
@@ -317,12 +496,12 @@
     namingContexts.resetLists();
 
     // a registered workflow with no parent is a naming context
-    for (WorkflowTopologyNode curWorkflow: registeredWorkflows)
+    for (WorkflowTopologyNode workflowNode: registeredWorkflowNodes.values())
     {
-      WorkflowTopologyNode parent = curWorkflow.getParent();
+      WorkflowTopologyNode parent = workflowNode.getParent();
       if (parent == null)
       {
-        namingContexts.addNamingContext (curWorkflow);
+        namingContexts.addNamingContext (workflowNode);
       }
     }
   }
@@ -336,7 +515,7 @@
    * @return <code>false</code> if the base DN is registered with the
    *         network group, <code>false</code> otherwise
    */
-  private boolean baseDNAlreadyRegistered (
+  private boolean baseDNAlreadyRegistered(
       DN baseDN
       )
   {
@@ -345,9 +524,9 @@
 
     // go through the list of registered workflow and check whether a base DN
     // has already been used in a registered workflow
-    for (WorkflowTopologyNode curWorkflow: registeredWorkflows)
+    for (WorkflowTopologyNode workflowNode: registeredWorkflowNodes.values())
     {
-      DN curDN = curWorkflow.getBaseDN();
+      DN curDN = workflowNode.getBaseDN();
       if (baseDN.equals (curDN))
       {
         alreadyRegistered = true;
@@ -377,28 +556,28 @@
    * @param  leftMargin  white spaces used to indent traces
    * @return a string buffer that contains trace information
    */
-  public StringBuffer toString (String leftMargin)
+  public StringBuffer toString(String leftMargin)
   {
     StringBuffer sb = new StringBuffer();
     String newMargin = leftMargin + "   ";
 
-    sb.append (leftMargin + "Networkgroup (" + networkGroupName+ "\n");
+    sb.append (leftMargin + "Networkgroup (" + networkGroupID+ "\n");
     sb.append (leftMargin + "List of registered workflows:\n");
-    for (WorkflowTopologyNode w: registeredWorkflows)
+    for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
     {
-      sb.append (w.toString (newMargin));
+      sb.append (node.toString (newMargin));
     }
 
     namingContexts.toString (leftMargin);
 
     sb.append (leftMargin + "rootDSEWorkflow:\n");
-    if (rootDSEWorkflow == null)
+    if (rootDSEWorkflowNode == null)
     {
       sb.append (newMargin + "null\n");
     }
     else
     {
-      sb.append (rootDSEWorkflow.toString (newMargin));
+      sb.append (rootDSEWorkflowNode.toString (newMargin));
     }
 
     return sb;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java b/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java
index e811757..f28841d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java
@@ -27,8 +27,16 @@
 package org.opends.server.core;
 
 
+import static org.opends.server.messages.CoreMessages.*;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.TreeMap;
+
 import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Operation;
+import org.opends.server.types.ResultCode;
 import org.opends.server.workflowelement.WorkflowElement;
 
 
@@ -43,6 +51,8 @@
  */
 public class WorkflowImpl implements Workflow
 {
+  // The workflow identifier used by the configuration.
+  private String workflowID = null;
 
   // The root of the workflow task tree.
   private WorkflowElement rootWorkflowElement = null;
@@ -63,6 +73,13 @@
   // flag will always return false.
   private boolean isPrivate = false;
 
+  // The set of workflows registered with the server.
+  private static TreeMap<String, Workflow> registeredWorkflows =
+    new TreeMap<String, Workflow>();
+
+  // A lock to protect concurrent access to the registeredWorkflows.
+  private static Object registeredWorkflowsLock = new Object();
+
 
   /**
    * Creates a new instance of a workflow implementation. To define a worfklow
@@ -71,14 +88,17 @@
    *
    * The rootWorkflowElement must not be null.
    *
+   * @param workflowId          workflow internal identifier
    * @param baseDN              identifies the data handled by the workflow
    * @param rootWorkflowElement the root node of the workflow task tree
    */
   public WorkflowImpl(
+      String          workflowId,
       DN              baseDN,
       WorkflowElement rootWorkflowElement
       )
   {
+    this.workflowID = workflowId;
     this.baseDN = baseDN;
     this.rootWorkflowElement = rootWorkflowElement;
     if (this.rootWorkflowElement != null)
@@ -100,6 +120,17 @@
 
 
   /**
+   * Gets the workflow internal identifier.
+   *
+   * @return the workflow internal indentifier
+   */
+  public String getWorkflowId()
+  {
+    return workflowID;
+  }
+
+
+  /**
    * Indicates whether the root node of the workflow task tree is
    * handling a private local backend.
    *
@@ -124,4 +155,79 @@
   {
     rootWorkflowElement.execute(operation);
   }
+
+
+  /**
+   * Registers the current worklow (this) with the server.
+   *
+   * @throws  DirectoryException  If the workflow ID for the provided workflow
+   *                              conflicts with the workflow ID of an existing
+   *                              workflow.
+   */
+  public void register()
+      throws DirectoryException
+  {
+    ensureNotNull(workflowID);
+
+    synchronized (registeredWorkflowsLock)
+    {
+      // The workflow must not be already registered
+      if (registeredWorkflows.containsKey(workflowID))
+      {
+        int msgID = MSGID_REGISTER_WORKFLOW_ALREADY_EXISTS;
+        String message = getMessage(msgID, workflowID);
+        throw new DirectoryException(
+            ResultCode.UNWILLING_TO_PERFORM, message, msgID);
+      }
+
+      TreeMap<String, Workflow> newRegisteredWorkflows =
+        new TreeMap<String, Workflow>(registeredWorkflows);
+      newRegisteredWorkflows.put(workflowID, this);
+      registeredWorkflows = newRegisteredWorkflows;
+    }
+  }
+
+
+  /**
+   * Deregisters the current worklow (this) with the server.
+   */
+  public void deregister()
+  {
+    ensureNotNull(workflowID);
+
+    synchronized (registeredWorkflowsLock)
+    {
+      TreeMap<String, Workflow> newWorkflows =
+        new TreeMap<String, Workflow>(registeredWorkflows);
+      newWorkflows.remove(workflowID);
+      registeredWorkflows = newWorkflows;
+    }
+  }
+
+
+  /**
+   * Deregisters a worklow with the server. The workflow to deregister
+   * is identified with its identifier.
+   *
+   * @param workflowID  the identifier of the workflow to deregister
+   *
+   * @return the workflow that has been deregistered,
+   *         <code>null</code> if no workflow has been found.
+   */
+  public WorkflowImpl deregister(String workflowID)
+  {
+    WorkflowImpl workflowToDeregister = null;
+
+    synchronized (registeredWorkflowsLock)
+    {
+      if (registeredWorkflows.containsKey(workflowID))
+      {
+        workflowToDeregister =
+          (WorkflowImpl) registeredWorkflows.get(workflowID);
+        workflowToDeregister.deregister();
+      }
+    }
+
+    return workflowToDeregister;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index 4fd0e24..3bb47e3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6352,6 +6352,37 @@
 
 
   /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a workflow with an ID that is already registered with the server.
+   * This takes a single argument, which is the conflicting workflow ID.
+   */
+  public static final int MSGID_REGISTER_WORKFLOW_ALREADY_EXISTS =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 635;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a workflow node with an ID that is already registered with a
+   * network group.  This takes two arguments, which are the conflicting
+   * workflow ID and the network group ID.
+   */
+  public static final int MSGID_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 636;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * register a network group with an ID that is already registered.  This
+   * takes a single argument, which is the conflicting network group ID.
+   */
+  public static final int MSGID_REGISTER_NETWORK_GROUP_ALREADY_EXISTS =
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 637;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined
    * in this class.
    */
@@ -8655,6 +8686,18 @@
     registerMessage(MSGID_IDLETIME_UNEXPECTED_ERROR,
                     "An unexpected error occurred in the idle time limit " +
                     "thread:  %s");
+    registerMessage(MSGID_REGISTER_WORKFLOW_ALREADY_EXISTS,
+                    "Unable to register workflow %s with the Directory " +
+                    "Server because another workflow with the same " +
+                    "workflow ID is already registered");
+    registerMessage(MSGID_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS,
+                    "Unable to register workflow node %s with the network " +
+                    "group %s because another workflow node with the same " +
+                    "workflow node ID is already registered");
+    registerMessage(MSGID_REGISTER_NETWORK_GROUP_ALREADY_EXISTS,
+                    "Unable to register network group %s with the Directory " +
+                    "Server because another network group with the same " +
+                    "network group ID is already registered");
   }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
index 098e06d..47a377b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
@@ -36,5 +36,14 @@
 public abstract class LeafWorkflowElement
   extends WorkflowElement
 {
-  // Empty at the moment.
+  /**
+   * Creates a new instance of the leaf workflow element.
+   *
+   * @param workflowElementID  the workflow element identifier as defined
+   *                           in the configuration.
+   */
+  protected LeafWorkflowElement(String workflowElementID)
+  {
+    super(workflowElementID);
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
index df604c3..fb1a6cc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
@@ -41,6 +41,10 @@
 public abstract class WorkflowElement
 {
 
+  // The workflow element identifier.
+  private String workflowElementID = null;
+
+
   /**
    * Indicates whether the workflow element encapsulates a private
    * local backend.
@@ -50,10 +54,13 @@
 
   /**
    * Creates a new instance of the workflow element.
+   *
+   * @param workflowElementID  the workflow element identifier as defined
+   *                           in the configuration.
    */
-  public WorkflowElement()
+  public WorkflowElement(String workflowElementID)
   {
-    // No implementation is required.
+    this.workflowElementID = workflowElementID;
   }
 
 
@@ -78,4 +85,15 @@
   {
     return isPrivate;
   }
+
+
+  /**
+   * Provides the workflow element identifier.
+   *
+   * @return the worflow element identifier
+   */
+  public String getWorkflowElementID()
+  {
+    return workflowElementID;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index b7ace2d..4bc2331 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -142,12 +142,16 @@
   /**
    * Creates a new instance of the local backend workflow element.
    *
+   * @param workflowElementID  the workflow element identifier
    * @param backend  the backend associated to that workflow element
    */
   public LocalBackendWorkflowElement(
+      String  workflowElementID,
       Backend backend
       )
   {
+    super(workflowElementID);
+
     this.backend  = backend;
 
     if (this.backend != null)
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
index 9819e76..2e0155b 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
@@ -27,6 +27,7 @@
 package org.opends.server.core;
 
 
+import static org.opends.server.messages.CoreMessages.*;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
@@ -41,10 +42,10 @@
 
 
 /**
- * This set of tests checks that network groups are properly created.
+ * This set of tests test the network groups.
  */
 public class NetworkGroupTest
-{
+{  
   //===========================================================================
   //
   //                      B E F O R E    C L A S S
@@ -72,6 +73,53 @@
   //===========================================================================
 
   /**
+   * Provides information to create a network group with one workflow inside.
+   *
+   * Each set of DNs contains:
+   * - one network group identifier
+   * - one base DN for the workflow to register with the network group
+
+   */
+  @DataProvider (name = "DNSet_0")
+  public Object[][] initDNSet_0()
+    throws Exception
+  {
+    // Network group ID
+    String networkGroupID1 = "networkGroup1";
+    String networkGroupID2 = "networkGroup2";
+
+    // Workflow base DNs
+    DN dn1 = null;
+    DN dn2 = null;
+    try
+    {
+      dn1 = DN.decode("o=test1");
+      dn2 = DN.decode("o=test2");
+    }
+    catch (DirectoryException de)
+    {
+      throw de;
+    }
+
+    // Network group info
+    Object[][] myData =
+    {
+        // Test1: create a network group with the identifier networkGroupID1
+        { networkGroupID1, dn1 },
+
+        // Test2: create the same network group to check that previous
+        // network group was properly cleaned.
+        { networkGroupID1, dn1 },
+
+        // Test3: create another network group
+        { networkGroupID2, dn2 },
+    };
+
+    return myData;
+  }
+
+
+  /**
    * Provides a single DN to search a workflow in a network group.
    * 
    * Each set of DNs is composed of:
@@ -137,8 +185,10 @@
     return myData;
   }
 
+
   /**
-   * Provides information to create a network group.
+   * Provides information to create a network group to test the routing
+   * process.
    *
    * Each set of DNs contains:
    * - one base DN for the 1st workflow
@@ -223,11 +273,76 @@
   //
   //===========================================================================
 
+  
   /**
-   * Gets a workflow candidate in the default network group.
+   * Tests the network group registration.
+   * 
+   * @param networkGroupID   the ID of the network group to register
+   * @param workflowBaseDN1  the base DN of the first workflow node to register
+   *                         in the network group
+   */
+  @Test (dataProvider = "DNSet_0", groups = "virtual")
+  public void testNetworkGroupRegistration(
+      String networkGroupID,
+      DN     workflowBaseDN
+      )
+      throws DirectoryException
+  {
+    // Create and register the network group with the server.
+    NetworkGroup networkGroup = new NetworkGroup(networkGroupID);
+    networkGroup.register();
+    
+    // Register again the network group with the server and catch the
+    // expected DirectoryServer exception.
+    boolean exceptionRaised = false;
+    try
+    {
+      networkGroup.register();
+    }
+    catch (DirectoryException de)
+    {
+      exceptionRaised = true;
+      assertEquals(
+          de.getMessageID(), MSGID_REGISTER_NETWORK_GROUP_ALREADY_EXISTS);
+    }
+    assertEquals(exceptionRaised, true);
+
+    // Create a workflow -- the workflow ID is the string representation
+    // of the workflow base DN.
+    WorkflowElement nullWE = null;
+    WorkflowImpl workflow = new WorkflowImpl(
+        workflowBaseDN.toString(), workflowBaseDN, nullWE);
+    
+    // Register the workflow with the network group.
+    networkGroup.registerWorkflow(workflow);
+    
+    // Register again the workflow with the network group and catch the
+    // expected DirectoryServer exception.
+    exceptionRaised = false;
+    try
+    {
+      networkGroup.registerWorkflow(workflow);
+    }
+    catch (DirectoryException de)
+    {
+      exceptionRaised = true;
+      assertEquals(
+          de.getMessageID(), MSGID_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS);
+    }
+    assertEquals(exceptionRaised, true);
+    
+    // Clean the network group
+    networkGroup.deregisterWorkflow(workflow.getWorkflowId());
+    networkGroup.deregister();
+  }
+
+
+  /**
+   * Check the route process in the default network group.
    *
-   *  @param dnToSearch     the DN of a workflow in the default network group
-   *  @param dnSubordinate  a subordinate of dnToSearch
+   *  @param dnToSearch     the DN of a workflow to search in the default
+   *                        network group
+   *  @param dnSubordinate  a subordinate DN of dnToSearch
    *  @param exists         true if we are supposed to find a workflow for
    *                        dnToSearch
    */
@@ -238,16 +353,121 @@
       boolean exists
       )
   {
-    // let's get the default network group (it should always exist)
+    // let's get the default network group -- it should always exist
     NetworkGroup defaultNG = NetworkGroup.getDefaultNetworkGroup();
     assertNotNull(defaultNG);
 
     // let's check the routing through the network group
-    doCheckNetworkGroup(defaultNG, dnToSearch, dnSubordinate, exists);
+    doCheckNetworkGroup(defaultNG, dnToSearch, dnSubordinate, null, exists);
 
-    // Dump info
-    StringBuffer sb = defaultNG.toString("defaultNetworkGroup> ");
-    writeln(sb.toString());
+    // Dump the default network group
+    dump(defaultNG, "defaultNetworkGroup> ");
+  }
+
+
+  /**
+   * Creates a network group with several workflows inside and do some check
+   * on the route processing.
+   *
+   * @param dn1           the DN for the 1st workflow
+   * @param dn2           the DN for the 2nd workflow
+   * @param dn3           the DN for the 3rd workflow
+   * @param subordinate1  the subordinate DN for the 1st workflow
+   * @param subordinate2  the subordinate DN for the 2nd workflow
+   * @param subordinate3  the subordinate DN for the 3rd workflow
+   * @param unrelatedDN   a DN with no hierarchical relationship with
+   *                      any of the DNs above
+   *
+   * @throws  DirectoryException  If the network group ID for a provided
+   *                              network group conflicts with the network
+   *                              group ID of an existing network group.
+   */
+  @Test (dataProvider = "DNSet_2", groups = "virtual")
+  public void createNetworkGroup(
+      DN dn1,
+      DN dn2,
+      DN dn3,
+      DN subordinate1,
+      DN subordinate2,
+      DN subordinate3,
+      DN unrelatedDN
+      ) throws DirectoryException
+  {
+    // The network group identifier is always the same for this test.
+    String networkGroupID = "Network Group for test2";
+
+    // Create the network group
+    NetworkGroup networkGroup = new NetworkGroup(networkGroupID);
+    assertNotNull(networkGroup);
+
+    // Register the network group with the server
+    networkGroup.register();
+
+    // Create and register workflow 1, 2 and 3
+    createAndRegisterWorkflow(networkGroup, dn1);
+    createAndRegisterWorkflow(networkGroup, dn2);
+    createAndRegisterWorkflow(networkGroup, dn3);
+
+    // Check the route thorugh the network group
+    doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, true);
+    doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, true);
+    doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, true);
+
+    // Deregister the workflow1 and check the route again.
+    // Workflow to deregister is identified by its baseDN.
+    networkGroup.deregisterWorkflow(dn1);
+    doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, false);
+    doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, true);
+    doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, true);
+
+    // Deregister the workflow2 and check the route again
+    networkGroup.deregisterWorkflow(dn2);
+    doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, false);
+    doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, false);
+    doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, true);
+
+    // Deregister the workflow3 and check the route again
+    networkGroup.deregisterWorkflow(dn3);
+    doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, false);
+    doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, false);
+    doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, false);
+
+    // Now create again the workflow 1, 2 and 3...
+    WorkflowImpl w1;
+    WorkflowImpl w2;
+    WorkflowImpl w3;
+    w1 = createAndRegisterWorkflow(networkGroup, dn1);
+    w2 = createAndRegisterWorkflow(networkGroup, dn2);
+    w3 = createAndRegisterWorkflow(networkGroup, dn3);
+
+    // ... and deregister the workflows using their workflowID
+    // instead of their baseDN
+    if (w1 != null)
+    {
+      networkGroup.deregisterWorkflow(w1.getWorkflowId());
+      doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, false);
+      doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, true);
+      doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, true);
+    }
+    
+    if (w2 != null)
+    {
+      networkGroup.deregisterWorkflow(w2.getWorkflowId());
+      doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, false);
+      doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, false);
+      doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, true);
+    }
+    
+    if (w3 != null)
+    {
+      networkGroup.deregisterWorkflow(w3.getWorkflowId());
+      doCheckNetworkGroup(networkGroup, dn1, subordinate1, unrelatedDN, false);
+      doCheckNetworkGroup(networkGroup, dn2, subordinate2, unrelatedDN, false);
+      doCheckNetworkGroup(networkGroup, dn3, subordinate3, unrelatedDN, false);
+    }
+    
+    // Deregister the network group
+    networkGroup.deregister();
   }
 
 
@@ -255,8 +475,11 @@
    * Checks the DN routing through a network group.
    *
    * @param networkGroup    the network group to use for the check
-   * @param dnToSearch      the DN of a workflow in the network group
+   * @param dnToSearch      the DN of a workflow in the network group; may
+   *                        be null
    * @param dnSubordinate   a subordinate of dnToSearch
+   * @param unrelatedDN     a DN with no hierarchical relationship with
+   *                        any of the DNs above, may be null
    * @param shouldExist     true if we are supposed to find a workflow for
    *                        dnToSearch
    */
@@ -264,9 +487,15 @@
       NetworkGroup networkGroup,
       DN           dnToSearch,
       DN           dnSubordinate,
+      DN           unrelatedDN,
       boolean      shouldExist
       )
   {
+    if (dnToSearch == null)
+    {
+      return;
+    }
+
     // Let's retrieve the workflow that maps best the dnToSearch
     Workflow workflow = networkGroup.getWorkflowCandidate(dnToSearch);
     if (shouldExist)
@@ -285,82 +514,14 @@
        Workflow workflow2 = networkGroup.getWorkflowCandidate(dnSubordinate);
        assertEquals(workflow2, workflow);
     }
-  }
-
-
-  /**
-   * Creates a network group with several workflows inside. Routing operation
-   * is done through the default root DSE workflow (which is automatically
-   * created by the network group class).
-   *
-   * @param dn1           the DN for the 1st workflow
-   * @param dn2           the DN for the 2nd workflow
-   * @param dn3           the DN for the 3rd workflow
-   * @param subordinate1  the subordinate DN for the 1st workflow
-   * @param subordinate2  the subordinate DN for the 2nd workflow
-   * @param subordinate3  the subordinate DN for the 3rd workflow
-   * @param unrelatedDN   a DN with no hierarchical relationship with
-   *                      any of the DNs above
-   */
-  @Test (dataProvider = "DNSet_2", groups = "virtual")
-  public void createNetworkGroup2(
-      DN dn1,
-      DN dn2,
-      DN dn3,
-      DN subordinate1,
-      DN subordinate2,
-      DN subordinate3,
-      DN unrelatedDN
-      )
-  {
-    String networkGroupName = "Network Group for test2";
-
-    // Create the network group
-    NetworkGroup ng = new NetworkGroup(networkGroupName);
-    assertNotNull(ng);
-
-    // Register the network group
-    NetworkGroup.registerNetworkGroup(ng);
-
-    // Create and register workflow 1
-    if (dn1 != null)
-    {
-      createAndRegisterWorkflow(ng, dn1, subordinate1, unrelatedDN);
-    }
-
-    // Create and register workflow 2
-    if (dn2 != null)
-    {
-      createAndRegisterWorkflow(ng, dn2, subordinate2, unrelatedDN);
-    }
-
-    // Create and register workflow 3
-    if (dn3 != null)
-    {
-      createAndRegisterWorkflow(ng, dn3, subordinate3, unrelatedDN);
-    }
-
-    // Dump info
-    StringBuffer sb = ng.toString("createNetworkGroup2(" + dn1 + ")> ");
-    writeln(sb.toString());
-
-    // Dump info of defaultNetworkGroup
-    StringBuffer sb2 =
-       NetworkGroup.getDefaultNetworkGroup().toString("defaultNetworkGroup> ");
-    writeln(sb2.toString());
     
-    // Deregister the workflow1...
-    deregisterWorkflow(ng, dn1, subordinate1, unrelatedDN);
-    
-    // ... and dump info again
-    sb = ng.toString("DEREGISTER Workflow> ");
-    writeln(sb.toString());
-
-    // dump info of defaultNetworkGroup
-    sb2 = NetworkGroup.getDefaultNetworkGroup().toString(
-        "DEREGISTER defaultNetworkGroup> "
-        );
-    writeln(sb2.toString());
+    // Check that the unrelatedDN is not handled by any workflow
+    if (unrelatedDN != null)
+    {
+      Workflow unrelatedWorkflow =
+        networkGroup.getWorkflowCandidate(unrelatedDN);
+      assertNull(unrelatedWorkflow);
+    }
   }
 
 
@@ -368,81 +529,36 @@
    * Creates a workflow and register it with a network group.
    *
    * @param networkGroup     a network group to register the workflow with
-   * @param workflowBaseDN   the base DN of the workflow to register
-   * @param subordinateDN    subordinate DN of the workflowBaseDN
-   * @param unrelatedDN      a DN with no hierarchical relationship with
-   *                         any of the base DN above
+   * @param workflowBaseDN   the base DN of the workflow to register; may be
+   *                         null
+   * @throws  DirectoryException  If the workflow ID for the provided
+   *                              workflow conflicts with the workflow
+   *                              ID of an existing workflow.
    */
-  private void createAndRegisterWorkflow(
+  private WorkflowImpl createAndRegisterWorkflow(
       NetworkGroup networkGroup,
-      DN           workflowBaseDN,
-      DN           subordinateDN,
-      DN           unrelatedDN
-      )
+      DN           workflowBaseDN
+      ) throws DirectoryException
   {
     assertNotNull(networkGroup);
 
-    // Create and register a workflow (no task in the workflow)
-    WorkflowElement rootWE = null;
-    WorkflowImpl realWorkflow = new WorkflowImpl(workflowBaseDN, rootWE);
-    assertNotNull(realWorkflow);
-
-    // Register the workflow with the network group.
-    networkGroup.registerWorkflow(realWorkflow);
-
-    // Now check that workflow is accessible through the network group
-    Workflow electedWorkflow;
-    electedWorkflow = networkGroup.getWorkflowCandidate(workflowBaseDN);
-    assertEquals(electedWorkflow.getBaseDN(), workflowBaseDN);
-
-    electedWorkflow = networkGroup.getWorkflowCandidate(subordinateDN);
-    assertEquals(electedWorkflow.getBaseDN(), workflowBaseDN);
-
-    // Check that the unrelatedDN is not handled by the workflow
-    Workflow unrelatedWorkflow =
-      networkGroup.getWorkflowCandidate(unrelatedDN);
-    assertNull(unrelatedWorkflow);
-  }
-  
-  
-  /**
-   * Deregisters a workflow with a network group. The workflow to
-   * deregister is identified by its baseDN.
-   *
-   * @param networkGroup     a network group that contains the workflow
-   *                         to deregister
-   * @param workflowBaseDN   the base DN of the workflow to deregister
-   * @param subordinateDN    subordinate DN of the workflowBaseDN
-   * @param unrelatedDN      a DN with no hierarchical relationship with
-   *                         any of the base DN above
-   */
-  private void deregisterWorkflow(
-      NetworkGroup networkGroup,
-      DN           workflowBaseDN,
-      DN           subordinateDN,
-      DN           unrelatedDN
-      )
-  {
-    assertNotNull(networkGroup);
-
-    // get the workflow in the network group
-    Workflow workflow = networkGroup.getWorkflowCandidate(workflowBaseDN);
-    if (workflow == null)
+    if (workflowBaseDN == null)
     {
-      // found no workflow
-      return;
+      return null;
     }
 
-    // Deregister the workflow with the network group
-    networkGroup.deregisterWorkflow(workflow.getBaseDN());
+    // Create a workflow with no task inside. The workflow identifier
+    // is the a string representation of the workflow base DN.
+    WorkflowElement rootWE = null;
+    String workflowId = workflowBaseDN.toString();
+    WorkflowImpl workflow = new WorkflowImpl(
+        workflowId, workflowBaseDN, rootWE);
+    assertNotNull(workflow);
 
-    // Check that the workflow is no more accessible through the network
-    // group
-    Workflow electedWorkflow;
-    electedWorkflow = networkGroup.getWorkflowCandidate(workflowBaseDN);
-    assertNull(electedWorkflow);
-    electedWorkflow = networkGroup.getWorkflowCandidate(subordinateDN);
-    assertNull(electedWorkflow);
+    // Register the workflow with the network group.
+    networkGroup.registerWorkflow(workflow);
+
+    return workflow;
   }
   
   
@@ -451,12 +567,7 @@
    */
   private void write(String msg)
   {
-    boolean dumpInfo = true;
-
-    if (dumpInfo)
-    {
-      System.out.print(msg);
-    }
+    System.out.print(msg);
   }
   
   
@@ -467,4 +578,21 @@
   {
     write(msg + "\n");
   }
+
+  
+  
+  /**
+   * Dump the network group info to the console.
+   */
+  private void dump(NetworkGroup networkGroup, String prompt)
+  {
+    final boolean doDump = false;
+
+    if (doDump)
+    {
+      StringBuffer sb = networkGroup.toString(prompt);
+      writeln(sb.toString());      
+    }
+  }
+
 }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java
index 265fe7c..310c374 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java
@@ -27,8 +27,10 @@
 package org.opends.server.core;
 
 
+import static org.opends.server.messages.CoreMessages.*;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotNull;
 
 import java.util.ArrayList;
 
@@ -428,7 +430,7 @@
    * @param dummyDN        a DN not registered in any workflow
    */
   @Test (dataProvider = "DNSet_1", groups = "virtual")
-  public void createWorkflow_basic (
+  public void createWorkflow_basic(
       DN baseDN,
       DN subordinateDN,
       DN dummyDN
@@ -436,30 +438,32 @@
   {
     // create a DIT set with the baseDN (no workflow element in the DIT).
     WorkflowElement nullWE = null;
-    WorkflowImpl realWorkflow = new WorkflowImpl (baseDN, nullWE);
+    WorkflowImpl workflow =
+      new WorkflowImpl (baseDN.toString(), baseDN, nullWE);
 
     // Create a worflow with the dit, no pre/post-workflow element.
-    WorkflowTopologyNode workflow = new WorkflowTopologyNode (realWorkflow, null, null);
+    WorkflowTopologyNode workflowNode =
+      new WorkflowTopologyNode (workflow, null, null);
 
     // The base DN in the workflow should match baseDN parameter
-    DN workflowBaseDN = workflow.getBaseDN();
+    DN workflowBaseDN = workflowNode.getBaseDN();
     assertEquals (workflowBaseDN, baseDN);
 
     // There should be no parent workflow.
-    WorkflowTopologyNode parent = workflow.getParent();
+    WorkflowTopologyNode parent = workflowNode.getParent();
     assertEquals (parent, null);
 
     // The workflow should handle the baseDN and subordinateDN.
     DN readBaseDN = null;
-    readBaseDN = workflow.getParentBaseDN (baseDN);
+    readBaseDN = workflowNode.getParentBaseDN (baseDN);
     assertEquals (readBaseDN, baseDN);
-    readBaseDN = workflow.getParentBaseDN (subordinateDN);
+    readBaseDN = workflowNode.getParentBaseDN (subordinateDN);
     assertEquals (readBaseDN, baseDN);
 
     // The workflow should not handle the dummyDN.
     if (dummyDN != null)
     {
-      readBaseDN = workflow.getParentBaseDN (dummyDN);
+      readBaseDN = workflowNode.getParentBaseDN (dummyDN);
       assertNull (readBaseDN);
     }
 
@@ -498,11 +502,11 @@
     WorkflowImpl unrelatedWorkflow = null;
     {
       WorkflowElement nullWE = null;
-      workflow    = new WorkflowImpl (baseDN, nullWE);
-      subWorkflow = new WorkflowImpl (subordinateDN, nullWE);
+      workflow = new WorkflowImpl (baseDN.toString(), baseDN, nullWE);
+      subWorkflow = new WorkflowImpl (subordinateDN.toString(), subordinateDN, nullWE);
       if (unrelatedDN != null)
       {
-        unrelatedWorkflow = new WorkflowImpl (unrelatedDN, nullWE);
+        unrelatedWorkflow = new WorkflowImpl (unrelatedDN.toString(), unrelatedDN, nullWE);
       }
     }
 
@@ -643,9 +647,9 @@
       WorkflowImpl workflow3;
       {
         WorkflowElement nullWE = null;
-        workflow1 = new WorkflowImpl (baseDN1, nullWE);
-        workflow2 = new WorkflowImpl (baseDN2, nullWE);
-        workflow3 = new WorkflowImpl(baseDN3, nullWE);
+        workflow1 = new WorkflowImpl (baseDN1.toString(), baseDN1, nullWE);
+        workflow2 = new WorkflowImpl (baseDN2.toString(), baseDN2, nullWE);
+        workflow3 = new WorkflowImpl (baseDN3.toString(), baseDN3, nullWE);
       }
 
       w1 = new WorkflowTopologyNode (workflow1, null, null);
@@ -803,7 +807,7 @@
    * @param unrelatedDN     a DN not registered in any workflow
    */
   @Test (dataProvider = "DNSet_3", groups = "virtual")
-  public void createWorkflow_complexTopology1 (
+  public void createWorkflow_complexTopology1(
       DN baseDN1,
       DN baseDN2,
       DN baseDN3,
@@ -825,9 +829,9 @@
       {
         WorkflowElement nullWE = null;
 
-        workflow1 = new WorkflowImpl (baseDN1, nullWE);
-        workflow2 = new WorkflowImpl (baseDN2, nullWE);
-        workflow3 = new WorkflowImpl (baseDN3, nullWE);
+        workflow1 = new WorkflowImpl (baseDN1.toString(), baseDN1, nullWE);
+        workflow2 = new WorkflowImpl (baseDN2.toString(), baseDN2, nullWE);
+        workflow3 = new WorkflowImpl (baseDN3.toString(), baseDN3, nullWE);
       }
 
       w1 = new WorkflowTopologyNode (workflow1, null, null);
@@ -935,4 +939,41 @@
         );
     assertEquals (globalResultCode.resultCode(), expectedGlobalResultCode);
   }
+
+
+  /**
+   * Tests the workflow registration.
+   */
+  @Test (dataProvider = "DNSet_1", groups = "virtual")
+  public void testWorkflowRegistration(
+      DN baseDN,
+      DN subordinateDN,
+      DN dummyDN
+      )
+      throws DirectoryException
+  {
+    WorkflowElement nullWE = null;
+
+    // Create a workflow to handle the baseDN with no workflow element
+    WorkflowImpl workflow = new WorkflowImpl(
+        baseDN.toString(), baseDN, nullWE);
+    
+    // Register the workflow with the server. Don't catch the
+    // DirectoryException that could be thrown by the register() method.
+    workflow.register();
+    
+    // Register the same workflow twice and catch the expected
+    // DirectoryException.
+    boolean exceptionRaised = false;
+    try
+    {
+      workflow.register();
+    }
+    catch (DirectoryException e)
+    {
+      exceptionRaised = true;
+      assertEquals(e.getMessageID(), MSGID_REGISTER_WORKFLOW_ALREADY_EXISTS);
+    }
+    assertEquals(exceptionRaised, true);
+  }
 }

--
Gitblit v1.10.0