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

jdemendi
08.57.2007 58a50508676500a29b29075f66e454b113a08791
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.

A workflow that is registered with a network group must have an identifier that is not already registered with the network group. To deregister a workflow one has to provide either a workflow base DN or a workflow identifier. The former method is used when a backend is deregistered with the server while the later may be used when applying workflow configuration changes.

The unit-tests are updated accordingly.

9 files modified
1109 ■■■■ changed files
opends/src/server/org/opends/server/core/DirectoryServer.java 39 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/NetworkGroup.java 361 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/WorkflowImpl.java 106 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java 43 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/WorkflowElement.java 22 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 4 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java 446 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java 77 ●●●● patch | view | raw | blame | history
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());
    }
  }
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;
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;
  }
}
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");
  }
}
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);
  }
}
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;
  }
}
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)
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());
    }
  }
}
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);
  }
}