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

Jean-Noel Rouvignac
25.56.2014 a47912185abb46815cc3104f2187bf63c22bcf03
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);
  }
}