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

Jean-Noel Rouvignac
25.56.2014 a47912185abb46815cc3104f2187bf63c22bcf03
OPENDJ-1545 (CR-5399) Remove Workflow, NetworkGroups and related attempts at building a proxy

Got rid of WorkflowTopology which is a parallel hierarchy of the BaseDnRegistry and the Backends which is an anti pattern (See Parallel Inheritance Hierarchy anti pattern, even though here it is more composition than inheritance at play).

Got rid of most of the Workflow and NetworkGroup classes my moving the important code to LocalBackendWorkflowElement.
Only remain bits and pieces of the old workflow design. We will need to see what we do with them (WorkflowResultCode, LocalBackendWorkflowElement naming?).



org/opends/server/core/networkgroups: REMOVED package

NetworkGroup.java, NetworkGroupNamingContexts.java, Workflow.java, WorkflowTopology.java, RootDseWorkflowTopology.java, WorkflowTopologyNode.java, WorkflowTopologyTest.java: REMOVED unused now


LocalBackendWorkflowElement.java:
Changed initialize(String, Backend<?>), createAndRegister(String, Backend), remove(String) and deregisterLocalBackend(String) to accept a DN argument instead of String.
Changed getWorkflowElementID() into getBaseDN().
Moved RootDseWorkflowTopology.execute() and WorkflowTopologyNode.execute() into this class as executeOnRootDSE() and executeOnNonRootDSE() + dependent methods. WorkflowTopology.execute() has been inlined into the moved code.
Removed useless fields workflowElementTypeInfo and BACKEND_WORKFLOW_ELEMENT.

AbstractOperation.java:
Added public updateOperationErrMsgAndResCode().

*OperationBasis.java:
Used LocalBackendWorkflowElement.execute(Operation, DN).
Made updateOperationErrMsgAndResCode() public.
Fixed javadocs.

WorkflowResultCode.java:
Made several methods public to use them in LocalBackendWorkflowElement.

NetworkGroupTest.java:
Renamed to org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElementTest.
Added testNonRootDseSubordinateNamingContext().
Removed several tests that do not make sense anymore after removing the NetworkGroup class.


RootDSEBackend.java:
In getRootDSE(), get the public naming context from the DirectoryServer rather than the NetworkGroup.
Removed getSubordinateNamingContexts().
Made getSubordinateBaseDNs() public.

DirectoryServer.java:
In createWorkflow(), create and register the LocalBackendWorkflowElement by using the baseDN instead of the backendId.
Removed references to the NetworkGroup and replaced them with references to the LocalBackendWorkflowElement where it made sense.
12 files modified
9 files deleted
1 files added
3936 ■■■■ changed files
opendj3-server-dev/src/server/org/opends/server/backends/RootDSEBackend.java 71 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/AddOperationBasis.java 41 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/BindOperationBasis.java 24 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/CompareOperationBasis.java 142 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/DeleteOperationBasis.java 38 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/DirectoryServer.java 19 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/ModifyDNOperationBasis.java 31 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/ModifyOperationBasis.java 38 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/RootDseWorkflowTopology.java 189 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/SearchOperationBasis.java 29 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/Workflow.java 67 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/WorkflowResultCode.java 10 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/WorkflowTopology.java 161 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/WorkflowTopologyNode.java 470 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/NetworkGroup.java 403 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/NetworkGroupNamingContexts.java 162 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/package-info.java 48 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/AbstractOperation.java 37 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 400 ●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java 845 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java 430 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java 281 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -31,7 +31,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -59,8 +58,6 @@
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.WorkflowTopologyNode;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.types.*;
import org.opends.server.util.BuildVersion;
import org.opends.server.util.LDIFWriter;
@@ -459,29 +456,13 @@
  {
    HashMap<AttributeType,List<Attribute>> dseUserAttrs =
         new HashMap<AttributeType,List<Attribute>>();
    HashMap<AttributeType,List<Attribute>> dseOperationalAttrs =
         new HashMap<AttributeType,List<Attribute>>();
    // Add the "namingContexts" attribute.
    final Collection<DN> namingContexts;
    if (connection == null)
    {
      namingContexts = DirectoryServer.getPublicNamingContexts().keySet();
    }
    else
    {
      namingContexts = new LinkedList<DN>();
      for (WorkflowTopologyNode node : NetworkGroup.getDefaultNetworkGroup()
          .getNamingContexts().getPublicNamingContexts())
      {
        namingContexts.add(node.getBaseDN());
      }
    }
    Attribute publicNamingContextAttr = createDNAttribute(ATTR_NAMING_CONTEXTS,
        ATTR_NAMING_CONTEXTS_LC, namingContexts);
    Attribute publicNamingContextAttr = createDNAttribute(
        ATTR_NAMING_CONTEXTS, ATTR_NAMING_CONTEXTS_LC,
        DirectoryServer.getPublicNamingContexts().keySet());
    addAttribute(publicNamingContextAttr, dseUserAttrs, dseOperationalAttrs);
@@ -680,45 +661,6 @@
  }
  /**
   * Determines the workflow nodes which handle subordinate naming contexts.
   * A workflow node is handling a subordinate naming context if the workflow
   * base DN is in the list of the RootDSE subordinate naming contexts.
   *
   * @param   nodes
   *          The list from which we search the workflow nodes which
   *          are handling subordinate naming contexts
   *
   * @return  The list of workflow nodes that are handling subordinate
   *          naming contexts
   */
  public Iterable<WorkflowTopologyNode> getSubordinateNamingContexts(
      Iterable<WorkflowTopologyNode> nodes)
  {
    // If the list of subordinate naming contexts is null
    // then return the whole list of workflow nodes.
    if (subordinateBaseDNs == null)
    {
      return nodes;
    }
    // The returned list of subordinate naming contexts
    List<WorkflowTopologyNode> subNC = new ArrayList<WorkflowTopologyNode>();
    // Determine which workflow node is handling a subordinate naming context.
    for (WorkflowTopologyNode node : nodes)
    {
      DN dn = node.getBaseDN();
      if (subordinateBaseDNs.containsKey(dn))
      {
        subNC.add(node);
      }
    }
    return subNC;
  }
  /**
   * Creates an attribute for the root DSE meant to hold a set of DNs.
   *
   * @param  name       The name for the attribute.
@@ -945,8 +887,13 @@
    }
  }
  /**
   * Returns the subordinate base DNs of the root DSE.
   *
   * @return the subordinate base DNs of the root DSE
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private Map<DN, Backend<?>> getSubordinateBaseDNs()
  public Map<DN, Backend<?>> getSubordinateBaseDNs()
  {
    if (subordinateBaseDNs != null)
    {
opendj3-server-dev/src/server/org/opends/server/core/AddOperationBasis.java
@@ -26,11 +26,6 @@
 */
package org.opends.server.core;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -41,7 +36,6 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.types.*;
@@ -49,6 +43,12 @@
import org.opends.server.types.operation.PreParseAddOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendAddOperation;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to add a new entry to the
 * Directory Server.
@@ -570,19 +570,7 @@
        return;
      }
      // Retrieve the network group attached to the client connection
      // and get a workflow to process the operation.
      Workflow workflow = NetworkGroup.getWorkflowCandidate(entryDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      workflowExecuted = true;
      workflowExecuted = execute(this, entryDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -633,8 +621,7 @@
   * elements of the workflow, otherwise invoke the post response plugins
   * that have been registered with the current operation.
   *
   * @param workflowExecuted <code>true</code> if a workflow has been
   *                         executed
   * @param workflowExecuted <code>true</code> if a workflow has been executed
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private void invokePostResponsePlugins(boolean workflowExecuted)
@@ -667,15 +654,9 @@
    }
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflows were found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  /** {@inheritDoc} */
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    DN entryDN = getEntryDN();
    DN parentDN = entryDN.getParentDNInSuffix();
opendj3-server-dev/src/server/org/opends/server/core/BindOperationBasis.java
@@ -35,7 +35,6 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.types.*;
import org.opends.server.types.operation.PreParseBindOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendBindOperation;
@@ -44,6 +43,7 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.core.DirectoryServer.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to authenticate a user to
@@ -557,16 +557,7 @@
          }
      }
      Workflow workflow = NetworkGroup.getWorkflowCandidate(bindDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      workflowExecuted = true;
      workflowExecuted = execute(this, bindDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -636,14 +627,9 @@
    }
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflows were found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  /** {@inheritDoc} */
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    LocalizableMessage message = ERR_BIND_OPERATION_UNKNOWN_USER.get();
    setResultCode(ResultCode.INVALID_CREDENTIALS);
opendj3-server-dev/src/server/org/opends/server/core/CompareOperationBasis.java
@@ -26,10 +26,6 @@
 */
package org.opends.server.core;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -40,12 +36,16 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.types.*;
import org.opends.server.types.operation.PostResponseCompareOperation;
import org.opends.server.types.operation.PreParseCompareOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendCompareOperation;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to determine whether a
 * specified entry in the Directory Server contains a given attribute-value
@@ -156,22 +156,14 @@
    attributeOptions       = new HashSet<String>();
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final ByteString getRawEntryDN()
  {
    return rawEntryDN;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final void setRawEntryDN(ByteString rawEntryDN)
  {
@@ -180,11 +172,7 @@
    entryDN = null;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final DN getEntryDN()
  {
@@ -204,22 +192,14 @@
    return entryDN;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final String getRawAttributeType()
  {
    return rawAttributeType;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final void setRawAttributeType(String rawAttributeType)
  {
@@ -229,8 +209,6 @@
    attributeOptions = null;
  }
  private void getAttributeTypeAndOptions() {
    String baseName;
    int semicolonPos = rawAttributeType.indexOf(';');
@@ -257,9 +235,7 @@
    attributeType = DirectoryServer.getAttributeType(baseName, true);
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final AttributeType getAttributeType()
  {
@@ -269,22 +245,14 @@
    return attributeType;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void setAttributeType(AttributeType attributeType)
  {
    this.attributeType = attributeType;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public Set<String> getAttributeOptions()
  {
@@ -294,43 +262,29 @@
    return attributeOptions;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void setAttributeOptions(Set<String> attributeOptions)
  {
    this.attributeOptions = attributeOptions;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final ByteString getAssertionValue()
  {
    return assertionValue;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public final void setAssertionValue(ByteString assertionValue)
  {
    this.assertionValue = assertionValue;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  /** {@inheritDoc} */
  @Override
  public final OperationType getOperationType()
  {
    // Note that no debugging will be done in this method because it is a likely
@@ -354,45 +308,29 @@
    return proxiedAuthorizationDN;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    this.proxiedAuthorizationDN = proxiedAuthorizationDN;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  /** {@inheritDoc} */
  @Override
  public final List<Control> getResponseControls()
  {
    return responseControls;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  /** {@inheritDoc} */
  @Override
  public final void addResponseControl(Control control)
  {
    responseControls.add(control);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  /** {@inheritDoc} */
  @Override
  public final void removeResponseControl(Control control)
  {
    responseControls.remove(control);
@@ -466,19 +404,7 @@
        return;
      }
      // Retrieve the network group registered with the client connection
      // and get a workflow to process the operation.
      Workflow workflow = NetworkGroup.getWorkflowCandidate(entryDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      workflowExecuted = true;
      workflowExecuted = execute(this, entryDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -520,11 +446,10 @@
  /**
   * Invokes the post response plugins. If a workflow has been executed
   * then invoke the post response plugins provided by the workflow
   * elements of the worklfow, otherwise invoke the post reponse plugins
   * elements of the workflow, otherwise invoke the post response plugins
   * that have been registered with the current operation.
   *
   * @param workflowExecuted <code>true</code> if a workflow has been
   *                         executed
   * @param workflowExecuted <code>true</code> if a workflow has been executed
   */
  private void invokePostResponsePlugins(boolean workflowExecuted)
  {
@@ -563,18 +488,15 @@
   * This method is called because no workflow was found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    setResultCode(ResultCode.NO_SUCH_OBJECT);
    appendErrorMessage(ERR_COMPARE_NO_SUCH_ENTRY.get(getEntryDN()));
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  /** {@inheritDoc} */
  @Override
  public final void toString(StringBuilder buffer)
  {
    buffer.append("CompareOperation(connID=");
opendj3-server-dev/src/server/org/opends/server/core/DeleteOperationBasis.java
@@ -26,8 +26,6 @@
 */
package org.opends.server.core;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.AccessLogger.*;
import java.util.ArrayList;
import java.util.List;
@@ -36,12 +34,15 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.types.*;
import org.opends.server.types.operation.PostResponseDeleteOperation;
import org.opends.server.types.operation.PreParseDeleteOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendDeleteOperation;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to remove an entry from the
 * Directory Server.
@@ -263,19 +264,7 @@
        return;
      }
      // Retrieve the network group attached to the client connection
      // and get a workflow to process the operation.
      Workflow workflow = NetworkGroup.getWorkflowCandidate(entryDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      workflowExecuted = true;
      workflowExecuted = execute(this, entryDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -323,11 +312,10 @@
  /**
   * Invokes the post response plugins. If a workflow has been executed
   * then invoke the post response plugins provided by the workflow
   * elements of the worklfow, otherwise invoke the post reponse plugins
   * elements of the workflow, otherwise invoke the post response plugins
   * that have been registered with the current operation.
   *
   * @param workflowExecuted <code>true</code> if a workflow has been
   *                         executed
   * @param workflowExecuted <code>true</code> if a workflow has been executed
   */
  private void invokePostResponsePlugins(boolean workflowExecuted)
  {
@@ -359,14 +347,9 @@
    }
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflows were found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  /** {@inheritDoc} */
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    setResultCode(ResultCode.NO_SUCH_OBJECT);
    appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(getEntryDN()));
@@ -384,4 +367,3 @@
  }
}
opendj3-server-dev/src/server/org/opends/server/core/DirectoryServer.java
@@ -125,7 +125,6 @@
import org.opends.server.config.JMXMBean;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.crypto.CryptoManagerImpl;
import org.opends.server.crypto.CryptoManagerSync;
import org.opends.server.extensions.ConfigFileHandler;
@@ -229,7 +228,6 @@
import static org.opends.server.util.DynamicConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines the core of the Directory Server.  It manages the startup
@@ -2162,12 +2160,7 @@
   */
  private static void createWorkflow(DN baseDN, Backend<?> backend) throws DirectoryException
  {
    // Create a root workflow element to encapsulate the backend
    final String backendID = backend.getBackendID();
    LocalBackendWorkflowElement rootWE = createAndRegister(backendID, backend);
    // Create the workflow for the base DN and register the workflow with the server
    NetworkGroup.getDefaultNetworkGroup().registerWorkflow(backendID, baseDN, rootWE);
    LocalBackendWorkflowElement.createAndRegister(baseDN, backend);
  }
  /**
@@ -5190,7 +5183,10 @@
      directoryServer.backends = newBackends;
      // Don't need anymore the local backend workflow element so we can remove it
      LocalBackendWorkflowElement.remove(backend.getBackendID());
      for (DN baseDN : backend.getBaseDNs())
      {
        LocalBackendWorkflowElement.remove(baseDN);
      }
      BackendMonitor monitor = backend.getBackendMonitor();
@@ -5391,7 +5387,7 @@
      // Now we need to deregister the workflow that was associated with the base DN
      if (!baseDN.equals(DN.valueOf("cn=config")))
      {
        NetworkGroup.getDefaultNetworkGroup().deregisterWorkflow(baseDN);
        LocalBackendWorkflowElement.remove(baseDN);
      }
    }
  }
@@ -7147,9 +7143,6 @@
        logger.traceException(e);
    }
    // Deregister all workflows and network group configuration.
    NetworkGroup.deregisterAllOnShutdown();
    // Force a new InternalClientConnection to be created on restart.
    InternalConnectionHandler.clearRootClientConnectionAtShutdown();
opendj3-server-dev/src/server/org/opends/server/core/ModifyDNOperationBasis.java
@@ -34,7 +34,6 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.types.*;
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import org.opends.server.types.operation.PreParseModifyDNOperation;
@@ -42,6 +41,7 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to alter the DN of an entry
@@ -449,18 +449,7 @@
        return;
      }
      // Retrieve the network group attached to the client connection
      // and get a workflow to process the operation.
      Workflow workflow = NetworkGroup.getWorkflowCandidate(entryDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      workflowExecuted = true;
      workflowExecuted = execute(this, entryDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -507,11 +496,10 @@
  /**
   * Invokes the post response plugins. If a workflow has been executed
   * then invoke the post response plugins provided by the workflow
   * elements of the worklfow, otherwise invoke the post reponse plugins
   * elements of the workflow, otherwise invoke the post response plugins
   * that have been registered with the current operation.
   *
   * @param workflowExecuted <code>true</code> if a workflow has been
   *                         executed
   * @param workflowExecuted <code>true</code> if a workflow has been executed
   */
  private void invokePostResponsePlugins(boolean workflowExecuted)
  {
@@ -545,14 +533,9 @@
    }
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflows were found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  /** {@inheritDoc} */
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    setResultCode(ResultCode.NO_SUCH_OBJECT);
    appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY.get(entryDN));
opendj3-server-dev/src/server/org/opends/server/core/ModifyOperationBasis.java
@@ -34,7 +34,6 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.protocols.ldap.LDAPModification;
import org.opends.server.protocols.ldap.LDAPResultCode;
@@ -45,6 +44,7 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to modify an entry in the
@@ -69,10 +69,7 @@
  /** The set of response controls for this modify operation. */
  private List<Control> responseControls;
  /**
   * The raw, unprocessed set of modifications as included in the client
   * request.
   */
  /** The raw, unprocessed set of modifications as included in the client request. */
  private List<RawModification> rawModifications;
  /** The set of modifications for this modify operation. */
@@ -368,19 +365,7 @@
        return;
      }
      // Retrieve the network group attached to the client connection
      // and get a workflow to process the operation.
      Workflow workflow = NetworkGroup.getWorkflowCandidate(entryDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      workflowExecuted = true;
      workflowExecuted = execute(this, entryDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -427,11 +412,10 @@
  /**
   * Invokes the post response plugins. If a workflow has been executed
   * then invoke the post response plugins provided by the workflow
   * elements of the worklfow, otherwise invoke the post reponse plugins
   * elements of the workflow, otherwise invoke the post response plugins
   * that have been registered with the current operation.
   *
   * @param workflowExecuted <code>true</code> if a workflow has been
   *                         executed
   * @param workflowExecuted <code>true</code> if a workflow has been executed
   */
  private void invokePostResponsePlugins(boolean workflowExecuted)
  {
@@ -464,15 +448,9 @@
    }
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflows were found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  /** {@inheritDoc} */
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    setResultCode(ResultCode.NO_SUCH_OBJECT);
    appendErrorMessage(ERR_MODIFY_NO_SUCH_ENTRY.get(getEntryDN()));
opendj3-server-dev/src/server/org/opends/server/core/RootDseWorkflowTopology.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -46,7 +46,6 @@
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.AccountUsableResponseControl;
import org.opends.server.controls.MatchedValuesControl;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.types.AbstractOperation;
import org.opends.server.types.Attribute;
@@ -74,6 +73,7 @@
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
/**
 * This class defines an operation that may be used to locate entries in the
@@ -889,9 +889,9 @@
    buffer.append(", baseDN=");
    buffer.append(rawBaseDN);
    buffer.append(", scope=");
    buffer.append(scope.toString());
    buffer.append(scope);
    buffer.append(", filter=");
    buffer.append(rawFilter.toString());
    buffer.append(rawFilter);
    buffer.append(")");
  }
@@ -1089,17 +1089,7 @@
        return;
      }
      // Retrieve the network group attached to the client connection
      // and get a workflow to process the operation.
      Workflow workflow = NetworkGroup.getWorkflowCandidate(baseDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        return;
      }
      workflow.execute(this);
      execute(this, baseDN);
    }
    catch(CanceledOperationException coe)
    {
@@ -1162,14 +1152,9 @@
    pluginConfigManager.invokePostResponseSearchPlugins(this);
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflows were found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  /** {@inheritDoc} */
  @Override
  public void updateOperationErrMsgAndResCode()
  {
    setResultCode(ResultCode.NO_SUCH_OBJECT);
    appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(getBaseDN()));
opendj3-server-dev/src/server/org/opends/server/core/Workflow.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/core/WorkflowResultCode.java
@@ -34,7 +34,7 @@
 * This class implements the workflow result code. The workflow result code
 * contains an LDAP result code along with an LDAP error message.
 */
class WorkflowResultCode
public class WorkflowResultCode
{
  /** The global result code. */
  private ResultCode resultCode = ResultCode.UNDEFINED;
@@ -58,7 +58,7 @@
   * @param resultCode    the initial value for the result code
   * @param errorMessage  the initial value for the error message
   */
  WorkflowResultCode(ResultCode resultCode, LocalizableMessageBuilder errorMessage)
  public WorkflowResultCode(ResultCode resultCode, LocalizableMessageBuilder errorMessage)
  {
    this.resultCode   = resultCode;
    this.errorMessage = errorMessage;
@@ -106,7 +106,7 @@
   * @param newErrorMessage  the new error message associated to the new error code
   * @return <code>true</code> if a referral result code must be turned into a reference entry
   */
  boolean elaborateGlobalResultCode(ResultCode newResultCode, LocalizableMessageBuilder newErrorMessage)
  public boolean elaborateGlobalResultCode(ResultCode newResultCode, LocalizableMessageBuilder newErrorMessage)
  {
    // if global result code has not been set yet then just take the new
    // result code as is
@@ -181,7 +181,7 @@
   *
   * @return the global result code.
   */
  ResultCode resultCode()
  public ResultCode resultCode()
  {
    return resultCode;
  }
@@ -191,7 +191,7 @@
   *
   * @return the global error message.
   */
  LocalizableMessageBuilder errorMessage()
  public LocalizableMessageBuilder errorMessage()
  {
    return errorMessage;
  }
opendj3-server-dev/src/server/org/opends/server/core/WorkflowTopology.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/core/WorkflowTopologyNode.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/NetworkGroupNamingContexts.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/package-info.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/types/AbstractOperation.java
@@ -72,35 +72,22 @@
  protected static final List<Control> NO_RESPONSE_CONTROLS =
       new ArrayList<Control>(0);
  /**
   * The client connection with which this operation is associated.
   */
  /** The client connection with which this operation is associated. */
  protected final ClientConnection clientConnection;
  /**
   * The message ID for this operation.
   */
  /** The message ID for this operation. */
  protected final int messageID;
  /**
   * The operation ID for this operation.
   */
  /** The operation ID for this operation. */
  protected final long operationID;
  /**
   * Whether nanotime was used for this operation.
   */
  /** Whether nanotime was used for this operation. */
  protected final boolean useNanoTime;
  /**
   * The cancel request for this operation.
   */
  /** The cancel request for this operation. */
  protected CancelRequest cancelRequest;
  /**
   * The cancel result for this operation.
   */
  /** The cancel result for this operation. */
  protected CancelResult cancelResult;
  /**
@@ -762,7 +749,6 @@
    {
      return true;
    }
    if (obj instanceof Operation)
    {
      Operation other = (Operation) obj;
@@ -771,7 +757,6 @@
        return other.getOperationID() == operationID;
      }
    }
    return false;
  }
@@ -799,5 +784,13 @@
      }
    }
  }
}
  /**
   * Updates the error message and the result code of the operation. This method
   * is called because no workflows were found to process the operation.
   */
  public void updateOperationErrMsgAndResCode()
  {
    // do nothing by default
  }
}
opendj3-server-dev/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -27,16 +27,20 @@
package org.opends.server.workflowelement.localbackend;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.LocalizableMessageDescriptor;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.api.AccessControlHandler;
import org.opends.server.api.Backend;
import org.opends.server.backends.RootDSEBackend;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
@@ -47,17 +51,6 @@
import static org.opends.messages.CoreMessages.*;
/**
 * This class defines a workflow element, i.e. a task in a workflow.
 *
 * [outdated]
 * A workflow element can wrap a physical
 * repository such as a local backend, a remote LDAP server or a local LDIF
 * file. A workflow element can also be used to route operations.
 * This is the case for load balancing and distribution.
 * And workflow element can be used in a virtual environment to transform data
 * (DN and attribute renaming, attribute value renaming...).
 * [/outdated]
 *
 * This class defines a local backend workflow element; e-g an entity that
 * handle the processing of an operation against a local backend.
 */
@@ -65,42 +58,30 @@
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * An information indicating the type of the current workflow element. This
   * information is for debug and tooling purpose only.
   */
  private String workflowElementTypeInfo = "not defined";
  /** The workflow element identifier. */
  private String workflowElementID;
  /** The backend's baseDN mapped by this object. */
  private DN baseDN;
  /** the backend associated with the local workflow element. */
  private Backend<?> backend;
  /** the set of local backend workflow elements registered with the server. */
  private static TreeMap<String, LocalBackendWorkflowElement> registeredLocalBackends =
      new TreeMap<String, LocalBackendWorkflowElement>();
  private static TreeMap<DN, LocalBackendWorkflowElement> registeredLocalBackends =
      new TreeMap<DN, LocalBackendWorkflowElement>();
  /** A lock to guarantee safe concurrent access to the registeredLocalBackends variable. */
  private static final Object registeredLocalBackendsLock = new Object();
  /** A string indicating the type of the workflow element. */
  private static final String BACKEND_WORKFLOW_ELEMENT = "Backend";
  /**
   * Initializes a new instance of the local backend workflow element.
   * This method is intended to be called by DirectoryServer when
   * workflow configuration mode is auto as opposed to
   * initializeWorkflowElement which is invoked when workflow
   * configuration mode is manual.
   *
   * @param workflowElementID  the workflow element identifier
   * @param backend  the backend associated to that workflow element
   * @param baseDN
   *          the backend's baseDN mapped by this object
   * @param backend
   *          the backend associated to that workflow element
   */
  private void initialize(String workflowElementID, Backend<?> backend)
  private void initialize(DN baseDN, Backend<?> backend)
  {
    this.workflowElementID = workflowElementID;
    this.workflowElementTypeInfo = BACKEND_WORKFLOW_ELEMENT;
    this.baseDN = baseDN;
    this.backend  = backend;
  }
@@ -122,30 +103,27 @@
   */
  public void finalizeWorkflowElement()
  {
    this.workflowElementID = null;
    this.workflowElementTypeInfo = null;
    this.baseDN = null;
    this.backend = null;
  }
  /**
   * Creates and registers a local backend with the server.
   *
   * @param workflowElementID  the identifier of the workflow element to create
   * @param backend            the backend to associate with the local backend
   *                           workflow element
   *
   * @return the existing local backend workflow element if it was
   *         already created or a newly created local backend workflow
   *         element.
   * @param baseDN
   *          the backend's baseDN mapped by this object
   * @param backend
   *          the backend to associate with the local backend workflow element
   * @return the existing local backend workflow element if it was already
   *         created or a newly created local backend workflow element.
   */
  public static LocalBackendWorkflowElement createAndRegister(
      String workflowElementID, Backend<?> backend)
  public static LocalBackendWorkflowElement createAndRegister(DN baseDN, Backend<?> backend)
  {
    LocalBackendWorkflowElement localBackend = registeredLocalBackends.get(workflowElementID);
    LocalBackendWorkflowElement localBackend = registeredLocalBackends.get(baseDN);
    if (localBackend == null)
    {
      localBackend = new LocalBackendWorkflowElement();
      localBackend.initialize(workflowElementID, backend);
      localBackend.initialize(baseDN, backend);
      registerLocalBackend(localBackend);
    }
@@ -157,11 +135,12 @@
  /**
   * Removes a local backend that was registered with the server.
   *
   * @param workflowElementID  the identifier of the workflow element to remove
   * @param baseDN
   *          the identifier of the workflow to remove
   */
  public static void remove(String workflowElementID)
  public static void remove(DN baseDN)
  {
    deregisterLocalBackend(workflowElementID);
    deregisterLocalBackend(baseDN);
  }
@@ -176,7 +155,7 @@
    {
      for (LocalBackendWorkflowElement localBackend : registeredLocalBackends.values())
      {
        deregisterLocalBackend(localBackend.getWorkflowElementID());
        deregisterLocalBackend(localBackend.getBaseDN());
      }
    }
  }
@@ -457,13 +436,13 @@
  {
    synchronized (registeredLocalBackendsLock)
    {
      String localBackendID = localBackend.getWorkflowElementID();
      LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(localBackendID);
      DN baseDN = localBackend.getBaseDN();
      LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(baseDN);
      if (existingLocalBackend == null)
      {
        TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
            new TreeMap<String, LocalBackendWorkflowElement>(registeredLocalBackends);
        newLocalBackends.put(localBackendID, localBackend);
        TreeMap<DN, LocalBackendWorkflowElement> newLocalBackends =
            new TreeMap<DN, LocalBackendWorkflowElement>(registeredLocalBackends);
        newLocalBackends.put(baseDN, localBackend);
        registeredLocalBackends = newLocalBackends;
      }
    }
@@ -474,25 +453,26 @@
  /**
   * Deregisters a local backend with the server.
   *
   * @param workflowElementID  the identifier of the workflow element to remove
   * @param baseDN
   *          the identifier of the local backend to remove
   */
  private static void deregisterLocalBackend(String workflowElementID)
  private static void deregisterLocalBackend(DN baseDN)
  {
    synchronized (registeredLocalBackendsLock)
    {
      LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(workflowElementID);
      LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(baseDN);
      if (existingLocalBackend != null)
      {
        TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
            new TreeMap<String, LocalBackendWorkflowElement>(registeredLocalBackends);
        newLocalBackends.remove(workflowElementID);
        TreeMap<DN, LocalBackendWorkflowElement> newLocalBackends =
            new TreeMap<DN, LocalBackendWorkflowElement>(registeredLocalBackends);
        newLocalBackends.remove(baseDN);
        registeredLocalBackends = newLocalBackends;
      }
    }
  }
  /**
   * Executes the workflow element for an operation.
   * Executes the workflow for an operation.
   *
   * @param operation
   *          the operation to execute
@@ -576,9 +556,9 @@
   *
   * @return the workflow element identifier
   */
  public String getWorkflowElementID()
  public DN getBaseDN()
  {
    return workflowElementID;
    return baseDN;
  }
  /**
@@ -641,13 +621,299 @@
    }
  }
  /**
   * Executes the supplied operation.
   *
   * @param operation
   *          the operation to execute
   * @param entryDN
   *          the entry DN whose backend will be used
   * @return true if the operation successfully executed, false otherwise
   * @throws CanceledOperationException
   *           if this operation should be cancelled.
   */
  public static boolean execute(Operation operation, DN entryDN) throws CanceledOperationException
  {
    LocalBackendWorkflowElement workflow = getLocalBackendWorkflowElement(entryDN);
    if (workflow == null)
    {
      // We have found no backend for the requested base DN,
      // just return a no such entry result code and stop the processing.
      if (operation instanceof AbstractOperation)
      {
        ((AbstractOperation) operation).updateOperationErrMsgAndResCode();
      }
      return false;
    }
    if (workflow.getBaseDN().isRootDN())
    {
      executeOnRootDSE(operation, workflow);
    }
    else
    {
      executeOnNonRootDSE(operation, workflow);
    }
    return true;
  }
  private static LocalBackendWorkflowElement getLocalBackendWorkflowElement(DN entryDN)
  {
    while (entryDN != null)
    {
      final LocalBackendWorkflowElement workflow = registeredLocalBackends.get(entryDN);
      if (workflow != null)
      {
        return workflow;
      }
      entryDN = entryDN.parent();
    }
    return null;
  }
  /**
   * Executes an operation on the root DSE entry.
   *
   * @param operation
   *          the operation to execute
   * @param workflow
   *          the workflow where to execute the operation
   * @throws CanceledOperationException
   *           if this operation should be cancelled.
   */
  private static void executeOnRootDSE(Operation operation, LocalBackendWorkflowElement workflow)
      throws CanceledOperationException
  {
    OperationType operationType = operation.getOperationType();
    if (operationType == OperationType.SEARCH)
    {
      executeSearch((SearchOperation) operation, workflow);
    }
    else
    {
      workflow.execute(operation);
    }
  }
  /**
   * Executes a search operation on the the root DSE entry.
   *
   * @param searchOp
   *          the operation to execute
   * @param workflow
   *          the workflow where to execute the operation
   * @throws CanceledOperationException
   *           if this operation should be cancelled.
   */
  private static void executeSearch(SearchOperation searchOp, LocalBackendWorkflowElement workflow)
      throws CanceledOperationException
  {
    // Keep a the original search scope because we will alter it in the operation
    SearchScope originalScope = searchOp.getScope();
    // Search base?
    // The root DSE entry itself is never returned unless the operation
    // is a search base on the null suffix.
    if (originalScope == SearchScope.BASE_OBJECT)
    {
      workflow.execute(searchOp);
      return;
    }
    // Create a workflow result code in case we need to perform search in
    // subordinate workflows.
    WorkflowResultCode workflowResultCode =
        new WorkflowResultCode(searchOp.getResultCode(), searchOp.getErrorMessage());
    // The search scope is not 'base', so let's do a search on all the public
    // naming contexts with appropriate new search scope and new base DN.
    SearchScope newScope = elaborateScopeForSearchInSubordinates(originalScope);
    searchOp.setScope(newScope);
    DN originalBaseDN = searchOp.getBaseDN();
    for (LocalBackendWorkflowElement subordinate : getRootDSESubordinates())
    {
      // We have to change the operation request base DN to match the
      // subordinate workflow base DN. Otherwise the workflow will
      // return a no such entry result code as the operation request
      // base DN is a superior of the workflow base DN!
      DN ncDN = subordinate.getBaseDN();
      // Set the new request base DN then do execute the operation
      // in the naming context workflow.
      searchOp.setBaseDN(ncDN);
      execute(searchOp, ncDN);
      boolean sendReferenceEntry = workflowResultCode.elaborateGlobalResultCode(
          searchOp.getResultCode(), searchOp.getErrorMessage());
      if (sendReferenceEntry)
      {
        // TODO jdemendi - turn a referral result code into a reference entry
        // and send the reference entry to the client application
      }
    }
    // Now restore the original request base DN and original search scope
    searchOp.setBaseDN(originalBaseDN);
    searchOp.setScope(originalScope);
    // If the result code is still uninitialized (ie no naming context),
    // we should return NO_SUCH_OBJECT
    workflowResultCode.elaborateGlobalResultCode(
        ResultCode.NO_SUCH_OBJECT, new LocalizableMessageBuilder(LocalizableMessage.EMPTY));
    // Set the operation result code and error message
    searchOp.setResultCode(workflowResultCode.resultCode());
    searchOp.setErrorMessage(workflowResultCode.errorMessage());
  }
  private static Collection<LocalBackendWorkflowElement> getRootDSESubordinates()
  {
    final RootDSEBackend rootDSEBackend = DirectoryServer.getRootDSEBackend();
    final List<LocalBackendWorkflowElement> results = new ArrayList<LocalBackendWorkflowElement>();
    for (DN subordinateBaseDN : rootDSEBackend.getSubordinateBaseDNs().keySet())
    {
      results.add(registeredLocalBackends.get(subordinateBaseDN));
    }
    return results;
  }
  private static void executeOnNonRootDSE(Operation operation, LocalBackendWorkflowElement workflow)
      throws CanceledOperationException
  {
    workflow.execute(operation);
    // For subtree search operation we need to go through the subordinate nodes.
    if (operation.getOperationType() == OperationType.SEARCH)
    {
      executeSearchOnSubordinates((SearchOperation) operation, workflow);
    }
  }
  /**
   * Executes a search operation on the subordinate workflows.
   *
   * @param searchOp
   *          the search operation to execute
   * @param workflow
   *          the workflow element
   * @throws CanceledOperationException
   *           if this operation should be canceled.
   */
  private static void executeSearchOnSubordinates(SearchOperation searchOp, LocalBackendWorkflowElement workflow)
      throws CanceledOperationException {
    // If the scope of the search is 'base' then it's useless to search
    // in the subordinate workflows.
    SearchScope originalScope = searchOp.getScope();
    if (originalScope == SearchScope.BASE_OBJECT)
    {
      return;
    }
    // Elaborate the new search scope before executing the search operation
    // in the subordinate workflows.
    SearchScope newScope = elaborateScopeForSearchInSubordinates(originalScope);
    searchOp.setScope(newScope);
    // Let's search in the subordinate workflows.
    WorkflowResultCode workflowResultCode = new WorkflowResultCode(
        searchOp.getResultCode(), searchOp.getErrorMessage());
    DN originalBaseDN = searchOp.getBaseDN();
    for (LocalBackendWorkflowElement subordinate : getSubordinates(workflow))
    {
      // We have to change the operation request base DN to match the
      // subordinate workflow base DN. Otherwise the workflow will
      // return a no such entry result code as the operation request
      // base DN is a superior of the subordinate workflow base DN.
      DN subordinateDN = subordinate.getBaseDN();
      // If the new search scope is 'base' and the search base DN does not
      // map the subordinate workflow then skip the subordinate workflow.
      if (newScope == SearchScope.BASE_OBJECT && !subordinateDN.parent().equals(originalBaseDN))
      {
        continue;
      }
      // If the request base DN is not a subordinate of the subordinate
      // workflow base DN then do not search in the subordinate workflow.
      if (!originalBaseDN.isAncestorOf(subordinateDN))
      {
        continue;
      }
      // Set the new request base DN and do execute the
      // operation in the subordinate workflow.
      searchOp.setBaseDN(subordinateDN);
      execute(searchOp, subordinateDN);
      boolean sendReferenceEntry =
          workflowResultCode.elaborateGlobalResultCode(searchOp.getResultCode(), searchOp.getErrorMessage());
      if (sendReferenceEntry)
      {
        // TODO jdemendi - turn a referral result code into a reference entry
        // and send the reference entry to the client application
      }
    }
    // Now we are done with the operation, let's restore the original
    // base DN and search scope in the operation.
    searchOp.setBaseDN(originalBaseDN);
    searchOp.setScope(originalScope);
    // Update the operation result code and error message
    searchOp.setResultCode(workflowResultCode.resultCode());
    searchOp.setErrorMessage(workflowResultCode.errorMessage());
  }
  private static Collection<LocalBackendWorkflowElement> getSubordinates(LocalBackendWorkflowElement workflow)
  {
    final DN baseDN = workflow.getBaseDN();
    final Backend<?> backend = workflow.getBackend();
    final ArrayList<LocalBackendWorkflowElement> results = new ArrayList<LocalBackendWorkflowElement>();
    for (Backend<?> subordinate : backend.getSubordinateBackends())
    {
      for (DN subordinateDN : subordinate.getBaseDNs())
      {
        if (subordinateDN.isDescendantOf(baseDN))
        {
          results.add(registeredLocalBackends.get(subordinateDN));
        }
      }
    }
    return results;
  }
  /**
   * Elaborates a new search scope according to the current search scope. The
   * new scope is intended to be used for searches on subordinate workflows.
   *
   * @param currentScope
   *          the current search scope
   * @return the new scope to use for searches on subordinate workflows,
   *         <code>null</code> when current scope is 'base'
   */
  private static SearchScope elaborateScopeForSearchInSubordinates(SearchScope currentScope)
  {
    switch (currentScope.asEnum())
    {
    case BASE_OBJECT:
      return null;
    case SINGLE_LEVEL:
      return SearchScope.BASE_OBJECT;
    case SUBORDINATES:
    case WHOLE_SUBTREE:
      return SearchScope.WHOLE_SUBTREE;
    default:
      return currentScope;
    }
  }
  /** {@inheritDoc} */
  @Override
  public String toString()
  {
    return getClass().getSimpleName()
        + " backend=" + backend
        + " workflowElementID=" + this.workflowElementID
        + " workflowElementTypeInfo=" + this.workflowElementTypeInfo;
        + " backend=" + this.backend
        + " baseDN=" + this.baseDN;
  }
}
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java
File was deleted
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java
File was deleted
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java
New file
@@ -0,0 +1,281 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS.
 */
package org.opends.server.workflowelement.localbackend;
import java.util.ArrayList;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.protocols.internal.SearchRequest;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Modification;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.protocols.internal.Requests.*;
import static org.testng.Assert.*;
/**
 * This set of tests test the LocalBackendWorkflowElement.
 */
@SuppressWarnings("javadoc")
public class LocalBackendWorkflowElementTest extends DirectoryServerTestCase
{
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * This test checks that workflows are updated as appropriate when backend
   * base DNs are added or removed.
   * <p>
   * When a new backend base DN is added, the new suffix should be accessible
   * for the route process - ie. a workflow should be created and be a potential
   * candidate for the route process.
   * <p>
   * Similarly, when a backend base DN is removed its associated workflow should
   * be removed; subsequently, any request targeting the removed suffix should
   * be rejected and a no such entry status code be returned.
   */
  @Test
  public void testBackendBaseDNModification() throws Exception
  {
    String suffix = "dc=example,dc=com";
    String suffix2 = "o=workflow suffix";
    String backendBaseDNName = "ds-cfg-base-dn";
    // Initialize a backend with a base entry.
    TestCaseUtils.clearJEBackend(true, "userRoot", suffix);
    // Check that suffix is accessible while suffix2 is not.
    searchEntry(suffix, ResultCode.SUCCESS);
    searchEntry(suffix2, ResultCode.NO_SUCH_OBJECT);
    // Add a new suffix in the backend and create a base entry for the new suffix
    String backendConfigDN = "ds-cfg-backend-id=userRoot," + DN_BACKEND_BASE;
    modifyAttribute(backendConfigDN, ModificationType.ADD, backendBaseDNName, suffix2);
    addBaseEntry(suffix2, "workflow suffix");
    // Both old and new suffix should be accessible.
    searchEntry(suffix, ResultCode.SUCCESS);
    searchEntry(suffix2, ResultCode.SUCCESS);
    // Remove the new suffix...
    modifyAttribute(backendConfigDN, ModificationType.DELETE, backendBaseDNName, suffix2);
    // ...and check that the removed suffix is no more accessible.
    searchEntry(suffix, ResultCode.SUCCESS);
    searchEntry(suffix2, ResultCode.NO_SUCH_OBJECT);
    // Replace the suffix with suffix2 in the backend
    modifyAttribute(backendConfigDN, ModificationType.REPLACE, backendBaseDNName, suffix2);
    // Now none of the suffixes are accessible: this means the entries
    // under the old suffix are not moved to the new suffix.
    searchEntry(suffix, ResultCode.NO_SUCH_OBJECT);
    searchEntry(suffix2, ResultCode.NO_SUCH_OBJECT);
    // Add a base entry for the new suffix
    addBaseEntry(suffix2, "workflow suffix");
    // The new suffix is accessible while the old one is not.
    searchEntry(suffix, ResultCode.NO_SUCH_OBJECT);
    searchEntry(suffix2, ResultCode.SUCCESS);
    // Reset the configuration with previous suffix
    modifyAttribute(backendConfigDN, ModificationType.REPLACE, backendBaseDNName, suffix);
  }
  /**
   * This test checks that the workflow takes into account the subordinate
   * naming context defined in the RootDSEBackend.
   */
  @Test
  public void testNonRootDseSubordinateNamingContext() throws Exception
  {
    // Backends for the test
    String backendID1 = "test-dc-example-dc-com-subordinate1,dc=example,dc=com";
    String backendID2 = "test-dc-example-dc-com-subordinate2,dc=example,dc=com";
    String backend1 = "o=" + backendID1;
    String backend2 = "o=" + backendID2;
    try
    {
      TestCaseUtils.clearDataBackends();
      // At this point, the list of subordinate naming context is not defined
      // yet (null): any public backend should be visible. Create a backend
      // with a base entry and check that the test naming context is visible.
      TestCaseUtils.initializeMemoryBackend(backendID1, backend1, true);
      searchEntries("dc=example,dc=com", ResultCode.SUCCESS, 1);
      // Create another test backend and check that the new backend is visible
      TestCaseUtils.initializeMemoryBackend(backendID2, backend2, true);
      searchEntries("dc=example,dc=com", ResultCode.SUCCESS, 2);
    }
    finally
    {
      // Clean the test backends. There is no more naming context.
      TestCaseUtils.clearMemoryBackend(backendID1);
      TestCaseUtils.clearMemoryBackend(backendID2);
      searchEntries("dc=example,dc=com", ResultCode.NO_SUCH_OBJECT, 0);
    }
  }
  /**
   * This test checks that the workflow takes into account the subordinate
   * naming context defined in the RootDSEBackend.
   */
  @Test
  public void testRootDseSubordinateNamingContext() throws Exception
  {
    // Backends for the test
    String backend1 = "o=test-rootDSE-subordinate-naming-context-1";
    String backend2 = "o=test-rootDSE-subordinate-naming-context-2";
    String backendID1 = "test-rootDSE-subordinate-naming-context-1";
    String backendID2 = "test-rootDSE-subordinate-naming-context-2";
    try
    {
      TestCaseUtils.clearDataBackends();
      // At this point, the list of subordinate naming context is not defined
      // yet (null): any public backend should be visible. Create a backend
      // with a base entry and check that the test naming context is visible.
      TestCaseUtils.initializeMemoryBackend(backendID1, backend1, true);
      searchPublicNamingContexts(ResultCode.SUCCESS, 1);
      // Create another test backend and check that the new backend is visible
      TestCaseUtils.initializeMemoryBackend(backendID2, backend2, true);
      searchPublicNamingContexts(ResultCode.SUCCESS, 2);
      // Now put in the list of subordinate naming context the backend1 naming context.
      // This white list will prevent the backend2 to be visible.
      TestCaseUtils.dsconfig(
          "set-root-dse-backend-prop",
          "--set", "subordinate-base-dn:" + backend1);
      searchPublicNamingContexts(ResultCode.SUCCESS, 1);
      // === Cleaning
      // Reset the subordinate naming context list.
      // Both naming context should be visible again.
      TestCaseUtils.dsconfig(
          "set-root-dse-backend-prop",
          "--reset", "subordinate-base-dn");
      searchPublicNamingContexts(ResultCode.SUCCESS, 2);
    }
    finally
    {
      // Clean the test backends. There is no more naming context.
      TestCaseUtils.clearMemoryBackend(backendID1);
      TestCaseUtils.clearMemoryBackend(backendID2);
      searchPublicNamingContexts(ResultCode.NO_SUCH_OBJECT, 0);
    }
  }
  /**
   * Searches the list of naming contexts.
   *
   * @param expectedRC  the expected result code
   * @param expectedNamingContexts  the number of expected naming contexts
   */
  private void searchPublicNamingContexts(ResultCode expectedRC, int expectedNamingContexts) throws Exception
  {
    searchEntries("", expectedRC, expectedNamingContexts);
  }
  private void searchEntries(String baseDN, ResultCode expectedRC, int expectedNbEntries) throws DirectoryException
  {
    SearchRequest request = newSearchRequest(DN.valueOf(baseDN), SearchScope.SINGLE_LEVEL);
    SearchOperation search = getRootConnection().processSearch(request);
    assertEquals(search.getResultCode(), expectedRC);
    if (expectedRC == ResultCode.SUCCESS)
    {
      assertEquals(search.getEntriesSent(), expectedNbEntries);
    }
  }
  /**
   * Searches an entry on a given connection.
   *
   * @param baseDN the request base DN string
   * @param expectedRC the expected result code
   */
  private void searchEntry(String baseDN, ResultCode expectedRC) throws Exception
  {
    SearchRequest request = newSearchRequest(DN.valueOf(baseDN), SearchScope.BASE_OBJECT);
    SearchOperation search = getRootConnection().processSearch(request);
    assertEquals(search.getResultCode(), expectedRC);
  }
  /**
   * Creates a base entry for the given suffix.
   *
   * @param suffix      the suffix for which the base entry is to be created
   */
  private void addBaseEntry(String suffix, String namingAttribute) throws Exception
  {
    TestCaseUtils.addEntry(
        "dn: " + suffix,
        "objectClass: top",
        "objectClass: organization",
        "o: " + namingAttribute);
  }
  /**
   * Adds/Deletes/Replaces an attribute in a given entry.
   *
   * @param baseDN          the request base DN string
   * @param modType         the modification type (add/delete/replace)
   * @param attributeName   the name  of the attribute to add/delete/replace
   * @param attributeValue  the value of the attribute to add/delete/replace
   */
  private void modifyAttribute(String baseDN, ModificationType modType, String attributeName, String attributeValue)
      throws Exception
  {
    ArrayList<Modification> mods = new ArrayList<Modification>();
    Attribute attributeToModify = Attributes.create(attributeName, attributeValue);
    mods.add(new Modification(modType, attributeToModify));
    ModifyOperation modifyOperation = getRootConnection().processModify(DN.valueOf(baseDN), mods);
    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
  }
}