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

Jean-Noel Rouvignac
04.29.2014 d0a544cff5da1c24e59eed6ea56877ca430dca82
OPENDJ-1545 Remove Workflow, NetworkGroups and related attempts at building a proxy


Code cleanup to ease reading that code.
4 files modified
637 ■■■■ changed files
opendj3-server-dev/src/server/org/opends/server/core/RootDseWorkflowTopology.java 37 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/NetworkGroup.java 180 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 179 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java 241 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/core/RootDseWorkflowTopology.java
@@ -28,10 +28,13 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.core.networkgroups.NetworkGroupNamingContexts;
import org.opends.server.types.*;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.types.CanceledOperationException;
import org.opends.server.types.DN;
import org.opends.server.types.Operation;
import org.opends.server.types.OperationType;
/**
 * This class implements the workflow node that handles the root DSE entry.
@@ -43,9 +46,11 @@
public class RootDseWorkflowTopology extends WorkflowTopology
{
  // The naming contexts known by the root DSE. These naming contexts
  // are defined in the scope of a network group.
  private NetworkGroupNamingContexts namingContexts = null;
  /**
   * The naming contexts known by the root DSE. These naming contexts
   * are defined in the scope of a network group.
   */
  private NetworkGroupNamingContexts namingContexts;
  /**
@@ -75,19 +80,16 @@
   * be cancelled.
   */
  @Override
  public void execute(Operation operation)
      throws CanceledOperationException {
    // Execute the operation.
  public void execute(Operation operation) throws CanceledOperationException
  {
    OperationType operationType = operation.getOperationType();
    if (operationType != OperationType.SEARCH)
    if (operationType == OperationType.SEARCH)
    {
      // Execute the operation
      getWorkflowImpl().execute(operation);
      executeSearch((SearchOperation) operation);
    }
    else
    {
      // Execute the SEARCH operation
      executeSearch((SearchOperation) operation);
      getWorkflowImpl().execute(operation);
    }
  }
@@ -175,13 +177,10 @@
   */
  public StringBuilder toString(String leftMargin)
  {
    String workflowID = getWorkflowImpl().getWorkflowId();
    StringBuilder sb = new StringBuilder();
    // display the identifier and baseDN
    String workflowID = this.getWorkflowImpl().getWorkflowId();
    sb.append(leftMargin + "Workflow ID = " + workflowID + "\n");
    sb.append(leftMargin + "         baseDN:[ \"\" ]\n");
    sb.append(leftMargin).append("Workflow ID = ").append(workflowID).append("\n");
    sb.append(leftMargin).append("         baseDN:[ \"\" ]\n");
    return sb;
  }
opendj3-server-dev/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
@@ -72,7 +72,7 @@
    // Invalidate all NetworkGroups so they cannot accidentally be
    // used after a restart.
    defaultNetworkGroup.invalidate();
    defaultNetworkGroup = new NetworkGroup("default");
    defaultNetworkGroup = new NetworkGroup(DEFAULT_NETWORK_GROUP_NAME);
  }
  /**
@@ -101,10 +101,7 @@
  private TreeMap<String, WorkflowTopologyNode> registeredWorkflowNodes =
      new TreeMap<String, WorkflowTopologyNode>();
  /**
   * A lock to protect concurrent access to the registered Workflow
   * nodes.
   */
  /** A lock to protect concurrent access to the registered Workflow nodes. */
  private final Object registeredWorkflowNodesLock = new Object();
  /**
@@ -134,13 +131,12 @@
   */
  public Workflow deregisterWorkflow(DN baseDN)
  {
    Workflow workflow = null;
    if (baseDN == null)
    {
      return workflow;
      return null;
    }
    Workflow workflow = null;
    if (baseDN.isRootDN())
    {
      // deregister the rootDSE
@@ -175,10 +171,8 @@
    // group, update the reference counter of the workflow.
    if (workflow != null)
    {
      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
      workflowImpl.decrementReferenceCounter();
      ((WorkflowImpl) workflow).decrementReferenceCounter();
    }
    return workflow;
  }
@@ -210,47 +204,33 @@
  private Workflow getWorkflowCandidatePriv(DN baseDN)
  {
    // the top workflow to return
    Workflow workflowCandidate = null;
    // get the list of workflow candidates
    if (baseDN.isRootDN())
    {
      // The rootDSE workflow is the candidate.
      workflowCandidate = rootDSEWorkflowNode;
      return rootDSEWorkflowNode;
    }
    else
    // Search the highest workflow in the topology that can handle the baseDN.
    // First search the private workflows
    // The order is important to ensure that the admin network group
    // is not broken and can always find cn=config
    for (WorkflowTopologyNode curWorkflow : namingContexts.getPrivateNamingContexts())
    {
      // Search the highest workflow in the topology that can handle
      // the baseDN.
      //First search the private workflows
      // The order is important to ensure that the admin network group
      // is not broken and can always find cn=config
      for (WorkflowTopologyNode curWorkflow : namingContexts
          .getPrivateNamingContexts())
      WorkflowTopologyNode workflowCandidate = curWorkflow.getWorkflowCandidate(baseDN);
      if (workflowCandidate != null)
      {
        workflowCandidate = curWorkflow.getWorkflowCandidate(baseDN);
        if (workflowCandidate != null)
        {
          break;
        }
        return workflowCandidate;
      }
      // If not found, search the public
      if (workflowCandidate == null) {
        for (WorkflowTopologyNode curWorkflow : namingContexts
            .getPublicNamingContexts())
        {
          workflowCandidate = curWorkflow.getWorkflowCandidate(baseDN);
          if (workflowCandidate != null)
          {
            break;
          }
        }
      }
    }
    return workflowCandidate;
    // Not found, search the public
    for (WorkflowTopologyNode curWorkflow : namingContexts.getPublicNamingContexts())
    {
      WorkflowTopologyNode workflowCandidate = curWorkflow.getWorkflowCandidate(baseDN);
      if (workflowCandidate != null)
      {
        return workflowCandidate;
      }
    }
    return null;
  }
  /**
@@ -263,7 +243,7 @@
   *           If the base DN of the workflow is already present in the
   *           network group
   */
  private void checkWorkflowBaseDN(WorkflowTopologyNode workflowNode)
  private void checkNotRegistered(WorkflowTopologyNode workflowNode)
      throws DirectoryException
  {
    String workflowID = workflowNode.getWorkflowImpl().getWorkflowId();
@@ -276,12 +256,9 @@
      DN nodeBaseDN = node.getBaseDN();
      if (nodeBaseDN.equals(workflowNode.getBaseDN()))
      {
        // The base DN is already registered in the network group,
        // we must reject the registration request
        LocalizableMessage message =
            ERR_REGISTER_WORKFLOW_BASE_DN_ALREADY_EXISTS.get(
                workflowID, networkGroupID, node.getWorkflowImpl()
                    .getWorkflowId(), workflowNode.getWorkflowImpl().getBaseDN());
        LocalizableMessage message = ERR_REGISTER_WORKFLOW_BASE_DN_ALREADY_EXISTS.get(
            workflowID, networkGroupID, node.getWorkflowImpl().getWorkflowId(),
            workflowNode.getWorkflowImpl().getBaseDN());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }
@@ -292,36 +269,21 @@
   *
   * @param workflow
   *          the workflow node to deregister
   * @return <code>true</code> when the workflow has been successfully
   *         deregistered
   */
  private boolean deregisterWorkflow(Workflow workflow)
  private void deregisterWorkflow(Workflow workflow)
  {
    // true as soon as the workflow has been deregistered
    boolean deregistered = false;
    // Is it the rootDSE workflow?
    if (workflow == rootDSEWorkflowNode)
    {
      rootDSEWorkflowNode = null;
      deregistered = true;
    }
    else
    {
      // Deregister the workflow with the network group.
      WorkflowTopologyNode workflowNode = (WorkflowTopologyNode) workflow;
      deregisterWorkflowNode(workflowNode);
      deregistered = true;
      // The workflow to deregister is not the root DSE workflow.
      // Remove it from the workflow topology.
      workflowNode.remove();
      // Rebuild the list of naming context handled by the network group
      rebuildNamingContextList();
      return;
    }
    return deregistered;
    WorkflowTopologyNode workflowNode = (WorkflowTopologyNode) workflow;
    deregisterWorkflowNode(workflowNode);
    // Remove it from the workflow topology.
    workflowNode.remove();
    rebuildNamingContextList();
  }
@@ -368,12 +330,12 @@
    // reset lists of naming contexts
    namingContexts.resetLists();
    // a registered workflow with no parent is a naming context
    for (WorkflowTopologyNode workflowNode : registeredWorkflowNodes.values())
    {
      WorkflowTopologyNode parent = workflowNode.getParent();
      if (parent == null)
      {
        // a registered workflow with no parent is a naming context
        namingContexts.addNamingContext(workflowNode);
      }
    }
@@ -394,49 +356,35 @@
   */
  public void registerWorkflow(WorkflowImpl workflow) throws DirectoryException
  {
    // Is it the rootDSE workflow?
    DN baseDN = workflow.getBaseDN();
    if (baseDN.isRootDN())
    {
      // NOTE - The rootDSE workflow is stored with the
      // registeredWorkflows.
      rootDSEWorkflowNode =
          new RootDseWorkflowTopology(workflow, namingContexts);
      // NOTE - The rootDSE workflow is stored with the registeredWorkflows.
      rootDSEWorkflowNode = new RootDseWorkflowTopology(workflow, namingContexts);
      return;
    }
    else
    // Try to insert it in the workflow topology.
    WorkflowTopologyNode workflowNode = new WorkflowTopologyNode(workflow);
    registerWorkflowNode(workflowNode);
    // Now add the workflow in the workflow topology...
    for (WorkflowTopologyNode curNode : registeredWorkflowNodes.values())
    {
      // This workflow is not the rootDSE workflow. Try to insert it in
      // the workflow topology.
      WorkflowTopologyNode workflowNode = new WorkflowTopologyNode(workflow);
      // Register the workflow node with the network group. If the
      // workflow ID is already existing then an exception is raised.
      registerWorkflowNode(workflowNode);
      // Now add the workflow in the workflow topology...
      for (WorkflowTopologyNode curNode : registeredWorkflowNodes
          .values())
      if (
          // Try to insert the new workflow under an existing workflow...
          curNode.insertSubordinate(workflowNode)
          // ... or try to insert the existing workflow below the new workflow
          || workflowNode.insertSubordinate(curNode))
      {
        if (
            // Try to insert the new workflow under an existing workflow...
            curNode.insertSubordinate(workflowNode)
            // ... or try to insert the existing workflow below the new workflow
            || workflowNode.insertSubordinate(curNode))
        {
          // new workflow has been inserted in the topology
          continue;
        }
        // new workflow has been inserted in the topology
        continue;
      }
      // Rebuild the list of naming context handled by the network group
      rebuildNamingContextList();
      // Now that the workflow node has been registered with the network
      // group, update the reference counter of the workflow, unless
      // the network group is either default, or administration, or
      // internal network group.
      workflow.incrementReferenceCounter();
    }
    rebuildNamingContextList();
    workflow.incrementReferenceCounter();
  }
@@ -459,19 +407,13 @@
    synchronized (registeredWorkflowNodesLock)
    {
      // The workflow must not be already registered
      if (registeredWorkflowNodes.containsKey(workflowID))
      {
        LocalizableMessage message =
            ERR_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS.get(workflowID,
                networkGroupID);
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            message);
        LocalizableMessage message = ERR_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS.get(workflowID, networkGroupID);
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
      }
      // The workflow base DN should not be already present in the
      // network group. Bypass the check for the private workflows...
      checkWorkflowBaseDN(workflowNode);
      checkNotRegistered(workflowNode);
      // All is fine, let's register the workflow
      TreeMap<String, WorkflowTopologyNode> newRegisteredWorkflowNodes =
opendj3-server-dev/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -35,6 +35,7 @@
import org.forgerock.i18n.LocalizableMessageDescriptor;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.AccessControlHandler;
import org.opends.server.api.Backend;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPostReadResponseControl;
@@ -80,10 +81,7 @@
  private static TreeMap<String, LocalBackendWorkflowElement> registeredLocalBackends =
      new TreeMap<String, LocalBackendWorkflowElement>();
  /**
   * A lock to guarantee safe concurrent access to the registeredLocalBackends
   * variable.
   */
  /** 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. */
@@ -124,7 +122,6 @@
   */
  public void finalizeWorkflowElement()
  {
    // null all fields so that any use of the finalized object will raise a NPE
    this.workflowElementID = null;
    this.workflowElementTypeInfo = null;
    this.backend = null;
@@ -144,18 +141,14 @@
  public static LocalBackendWorkflowElement createAndRegister(
      String workflowElementID, Backend<?> backend)
  {
    // If the requested workflow element does not exist then create one.
    LocalBackendWorkflowElement localBackend =
        registeredLocalBackends.get(workflowElementID);
    LocalBackendWorkflowElement localBackend = registeredLocalBackends.get(workflowElementID);
    if (localBackend == null)
    {
      localBackend = new LocalBackendWorkflowElement();
      localBackend.initialize(workflowElementID, backend);
      // store the new local backend in the list of registered backends
      registerLocalBackend(localBackend);
    }
    return localBackend;
  }
@@ -181,8 +174,7 @@
  {
    synchronized (registeredLocalBackendsLock)
    {
      for (LocalBackendWorkflowElement localBackend:
           registeredLocalBackends.values())
      for (LocalBackendWorkflowElement localBackend : registeredLocalBackends.values())
      {
        deregisterLocalBackend(localBackend.getWorkflowElementID());
      }
@@ -220,8 +212,7 @@
      {
        final Control control = iter.next();
        if (!AccessControlConfigManager.getInstance().getAccessControlHandler()
            .isAllowed(targetDN, op, control))
        if (!getAccessControlHandler().isAllowed(targetDN, op, control))
        {
          // As per RFC 4511 4.1.11.
          if (control.isCritical())
@@ -278,14 +269,12 @@
      Entry entry, DN entryDN, ResultCode resultCode, LocalizableMessage message,
      ResultCode altResultCode, LocalizableMessage altMessage) throws DirectoryException
  {
    if (AccessControlConfigManager.getInstance().getAccessControlHandler()
        .canDiscloseInformation(entry, entryDN, operation))
    if (getAccessControlHandler().canDiscloseInformation(entry, entryDN, operation))
    {
      return new DirectoryException(resultCode, message);
    }
    // replacement reason returned to the user
    final DirectoryException ex =
        new DirectoryException(altResultCode, altMessage);
    final DirectoryException ex = new DirectoryException(altResultCode, altMessage);
    // real underlying reason
    ex.setMaskedResultCode(resultCode);
    ex.setMaskedMessage(message);
@@ -329,8 +318,7 @@
      Entry entry, DN entryDN, ResultCode resultCode, LocalizableMessage message,
      ResultCode altResultCode, LocalizableMessage altMessage) throws DirectoryException
  {
    if (AccessControlConfigManager.getInstance().getAccessControlHandler()
        .canDiscloseInformation(entry, entryDN, operation))
    if (getAccessControlHandler().canDiscloseInformation(entry, entryDN, operation))
    {
      operation.setResultCode(resultCode);
      operation.appendErrorMessage(message);
@@ -362,8 +350,7 @@
    try
    {
      if (!AccessControlConfigManager.getInstance().getAccessControlHandler()
          .canDiscloseInformation(null, operation.getMatchedDN(), operation))
      if (!getAccessControlHandler().canDiscloseInformation(null, operation.getMatchedDN(), operation))
      {
        operation.setMatchedDN(null);
      }
@@ -404,28 +391,19 @@
     */
    final Entry fullEntry = entry.duplicate(true);
    /*
     * Even though the associated update succeeded, we should still check
     * whether or not we should return the entry.
     */
    final SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(
        fullEntry, null);
    if (AccessControlConfigManager.getInstance().getAccessControlHandler()
        .maySend(operation, unfilteredSearchEntry))
    // Even though the associated update succeeded,
    // we should still check whether or not we should return the entry.
    final SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(fullEntry, null);
    if (getAccessControlHandler().maySend(operation, unfilteredSearchEntry))
    {
      // Filter the entry based on the control's attribute list.
      final Entry filteredEntry = fullEntry.filterEntry(
          postReadRequest.getRequestedAttributes(), false, false, false);
      final SearchResultEntry filteredSearchEntry = new SearchResultEntry(
          filteredEntry, null);
      final Entry filteredEntry = fullEntry.filterEntry(postReadRequest.getRequestedAttributes(), false, false, false);
      final SearchResultEntry filteredSearchEntry = new SearchResultEntry(filteredEntry, null);
      // Strip out any attributes which access control denies access to.
      AccessControlConfigManager.getInstance().getAccessControlHandler()
          .filterEntry(operation, unfilteredSearchEntry, filteredSearchEntry);
      getAccessControlHandler().filterEntry(operation, unfilteredSearchEntry, filteredSearchEntry);
      final LDAPPostReadResponseControl responseControl =
          new LDAPPostReadResponseControl(filteredSearchEntry);
      operation.addResponseControl(responseControl);
      operation.addResponseControl(new LDAPPostReadResponseControl(filteredSearchEntry));
    }
  }
@@ -449,52 +427,42 @@
      return;
    }
    /*
     * Even though the associated update succeeded, we should still check
     * whether or not we should return the entry.
     */
    final SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(
        entry, null);
    if (AccessControlConfigManager.getInstance().getAccessControlHandler()
        .maySend(operation, unfilteredSearchEntry))
    // Even though the associated update succeeded,
    // we should still check whether or not we should return the entry.
    final SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(entry, null);
    if (getAccessControlHandler().maySend(operation, unfilteredSearchEntry))
    {
      // Filter the entry based on the control's attribute list.
      final Entry filteredEntry = entry.filterEntry(
          preReadRequest.getRequestedAttributes(), false, false, false);
      final SearchResultEntry filteredSearchEntry = new SearchResultEntry(
          filteredEntry, null);
      final Entry filteredEntry = entry.filterEntry(preReadRequest.getRequestedAttributes(), false, false, false);
      final SearchResultEntry filteredSearchEntry = new SearchResultEntry(filteredEntry, null);
      // Strip out any attributes which access control denies access to.
      AccessControlConfigManager.getInstance().getAccessControlHandler()
          .filterEntry(operation, unfilteredSearchEntry, filteredSearchEntry);
      getAccessControlHandler().filterEntry(operation, unfilteredSearchEntry, filteredSearchEntry);
      final LDAPPreReadResponseControl responseControl =
          new LDAPPreReadResponseControl(filteredSearchEntry);
      operation.addResponseControl(responseControl);
      operation.addResponseControl(new LDAPPreReadResponseControl(filteredSearchEntry));
    }
  }
  private static AccessControlHandler<?> getAccessControlHandler()
  {
    return AccessControlConfigManager.getInstance().getAccessControlHandler();
  }
  /**
   * Registers a local backend with the server.
   *
   * @param localBackend  the local backend to register with the server
   */
  private static void registerLocalBackend(
                           LocalBackendWorkflowElement localBackend)
  private static void registerLocalBackend(LocalBackendWorkflowElement localBackend)
  {
    synchronized (registeredLocalBackendsLock)
    {
      String localBackendID = localBackend.getWorkflowElementID();
      LocalBackendWorkflowElement existingLocalBackend =
        registeredLocalBackends.get(localBackendID);
      LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(localBackendID);
      if (existingLocalBackend == null)
      {
        TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
          new TreeMap
            <String, LocalBackendWorkflowElement>(registeredLocalBackends);
            new TreeMap<String, LocalBackendWorkflowElement>(registeredLocalBackends);
        newLocalBackends.put(localBackendID, localBackend);
        registeredLocalBackends = newLocalBackends;
      }
@@ -512,14 +480,11 @@
  {
    synchronized (registeredLocalBackendsLock)
    {
      LocalBackendWorkflowElement existingLocalBackend =
        registeredLocalBackends.get(workflowElementID);
      LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(workflowElementID);
      if (existingLocalBackend != null)
      {
        TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
             new TreeMap<String, LocalBackendWorkflowElement>(
                      registeredLocalBackends);
            new TreeMap<String, LocalBackendWorkflowElement>(registeredLocalBackends);
        newLocalBackends.remove(workflowElementID);
        registeredLocalBackends = newLocalBackends;
      }
@@ -538,45 +503,31 @@
    switch (operation.getOperationType())
    {
      case BIND:
        LocalBackendBindOperation bindOperation =
             new LocalBackendBindOperation((BindOperation) operation);
        bindOperation.processLocalBind(this);
        new LocalBackendBindOperation((BindOperation) operation).processLocalBind(this);
        break;
      case SEARCH:
        LocalBackendSearchOperation searchOperation =
             new LocalBackendSearchOperation((SearchOperation) operation);
        searchOperation.processLocalSearch(this);
        new LocalBackendSearchOperation((SearchOperation) operation).processLocalSearch(this);
        break;
      case ADD:
        LocalBackendAddOperation addOperation =
             new LocalBackendAddOperation((AddOperation) operation);
        addOperation.processLocalAdd(this);
        new LocalBackendAddOperation((AddOperation) operation).processLocalAdd(this);
        break;
      case DELETE:
        LocalBackendDeleteOperation deleteOperation =
             new LocalBackendDeleteOperation((DeleteOperation) operation);
        deleteOperation.processLocalDelete(this);
        new LocalBackendDeleteOperation((DeleteOperation) operation).processLocalDelete(this);
        break;
      case MODIFY:
        LocalBackendModifyOperation modifyOperation =
             new LocalBackendModifyOperation((ModifyOperation) operation);
        modifyOperation.processLocalModify(this);
        new LocalBackendModifyOperation((ModifyOperation) operation).processLocalModify(this);
        break;
      case MODIFY_DN:
        LocalBackendModifyDNOperation modifyDNOperation =
             new LocalBackendModifyDNOperation((ModifyDNOperation) operation);
        modifyDNOperation.processLocalModifyDN(this);
        new LocalBackendModifyDNOperation((ModifyDNOperation) operation).processLocalModifyDN(this);
        break;
      case COMPARE:
        LocalBackendCompareOperation compareOperation =
             new LocalBackendCompareOperation((CompareOperation) operation);
        compareOperation.processLocalCompare(this);
        new LocalBackendCompareOperation((CompareOperation) operation).processLocalCompare(this);
        break;
      case ABANDON:
@@ -606,9 +557,7 @@
  @SuppressWarnings("unchecked")
  static <O extends Operation, L> void attachLocalOperation(O globalOperation, L currentLocalOperation)
  {
    List<?> existingAttachment =
      (List<?>) globalOperation.getAttachment(Operation.LOCALBACKENDOPERATIONS);
    List<?> existingAttachment = (List<?>) globalOperation.getAttachment(Operation.LOCALBACKENDOPERATIONS);
    List<L> newAttachment = new ArrayList<L>();
    if (existingAttachment != null)
@@ -619,8 +568,7 @@
      newAttachment.addAll ((List<L>) existingAttachment);
    }
    newAttachment.add (currentLocalOperation);
    globalOperation.setAttachment(Operation.LOCALBACKENDOPERATIONS,
                                  newAttachment);
    globalOperation.setAttachment(Operation.LOCALBACKENDOPERATIONS, newAttachment);
  }
  /**
@@ -672,32 +620,23 @@
  {
    if (!backend.isPrivateBackend())
    {
      switch (DirectoryServer.getWritabilityMode())
      checkIfWritable(DirectoryServer.getWritabilityMode(), op, serverMsg, entryDN);
      checkIfWritable(backend.getWritabilityMode(), op, backendMsg, entryDN);
    }
  }
  private static void checkIfWritable(WritabilityMode writabilityMode, Operation op,
      LocalizableMessageDescriptor.Arg1<Object> errorMsg, DN entryDN) throws DirectoryException
  {
    switch (writabilityMode)
    {
    case DISABLED:
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, errorMsg.get(entryDN));
    case INTERNAL_ONLY:
      if (!(op.isInternalOperation() || op.isSynchronizationOperation()))
      {
      case DISABLED:
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            serverMsg.get(String.valueOf(entryDN)));
      case INTERNAL_ONLY:
        if (!(op.isInternalOperation() || op.isSynchronizationOperation()))
        {
          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
              serverMsg.get(String.valueOf(entryDN)));
        }
      }
      switch (backend.getWritabilityMode())
      {
      case DISABLED:
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            backendMsg.get(String.valueOf(entryDN)));
      case INTERNAL_ONLY:
        if (!(op.isInternalOperation() || op.isSynchronizationOperation()))
        {
          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
              backendMsg.get(String.valueOf(entryDN)));
        }
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, errorMsg.get(entryDN));
      }
    }
  }
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java
@@ -60,15 +60,7 @@
 */
@SuppressWarnings("javadoc")
public class NetworkGroupTest extends DirectoryServerTestCase {
  //===========================================================================
  //                      B E F O R E    C L A S S
  //===========================================================================
  /**
   * Sets up the environment for performing the tests in this suite.
   *
   * @throws Exception if the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception
  {
@@ -77,10 +69,6 @@
    TestCaseUtils.startServer();
  }
  //===========================================================================
  //                      D A T A    P R O V I D E R
  //===========================================================================
  /**
   * Provides information to create a network group with one workflow inside.
   *
@@ -162,35 +150,6 @@
    };
  }
  /**
   * Provides information to create 2 network groups with different priorities.
   */
  @DataProvider (name = "DNSet_4")
  public Object[][] initDNSet_4() throws Exception
  {
    String networkGroupID1 = "group1";
    String networkGroupID2 = "group2";
    DN dn1 = DN.valueOf("o=test1");
    DN dn2 = DN.valueOf("o=test2");
    return new Object[][] {
      {
        networkGroupID1, dn1, 1,
        networkGroupID2, dn2, 2
      },
      {
        networkGroupID1, dn1, 2,
        networkGroupID2, dn2, 1
      }
    };
  }
  //===========================================================================
  //                        T E S T   C A S E S
  //===========================================================================
  /**
   * Tests the network group registration.
   *
@@ -199,11 +158,7 @@
   *                         in the network group
   */
  @Test (dataProvider = "DNSet_0", groups = "virtual")
  public void testNetworkGroupRegistration(
      String networkGroupID,
      DN     workflowBaseDN
      )
      throws Exception
  public void testNetworkGroupRegistration(String networkGroupID, DN workflowBaseDN) throws Exception
  {
    // Create and register the network group with the server.
    NetworkGroup networkGroup = new NetworkGroup(networkGroupID);
@@ -211,8 +166,6 @@
    // Create a workflow -- the workflow ID is the string representation
    // of the workflow base DN.
    WorkflowImpl workflow = new WorkflowImpl(workflowBaseDN.toString(), workflowBaseDN, null);
    // Register the workflow with the network group.
    networkGroup.registerWorkflow(workflow);
    // Register again the workflow with the network group and catch the
@@ -220,7 +173,7 @@
    try
    {
      networkGroup.registerWorkflow(workflow);
      fail("DirectoryException sjhould have been thrown");
      fail("DirectoryException should have been thrown");
    }
    catch (DirectoryException de)
    {
@@ -229,31 +182,6 @@
    }
  }
  /**
   * Check the route process in the default network group.
   *
   *  @param dnToSearch     the DN of a workflow to search in the default
   *                        network group
   *  @param dnSubordinate  a subordinate DN of dnToSearch
   *  @param exists         true if we are supposed to find a workflow for
   *                        dnToSearch
   */
  @Test (dataProvider = "DNSet_1", groups = "virtual")
  public void checkDefaultNetworkGroup(
      DN      dnToSearch,
      DN      dnSubordinate,
      boolean exists
      )
  {
    // let's get the default network group -- it should always exist
    NetworkGroup defaultNG = NetworkGroup.getDefaultNetworkGroup();
    assertNotNull(defaultNG);
    // let's check the routing through the network group
    doCheckNetworkGroup(defaultNG, dnToSearch, dnSubordinate, null, exists);
  }
  /**
   * This test checks that network groups are updated as appropriate when
   * backend base DNs are added or removed. When a new backend base DN is
@@ -265,8 +193,7 @@
   * returned.
   */
  @Test
  public void testBackendBaseDNModification()
         throws Exception
  public void testBackendBaseDNModification() throws Exception
  {
    String suffix  = "dc=example,dc=com";
    String suffix2 = "o=networkgroup suffix";
@@ -276,8 +203,8 @@
    TestCaseUtils.clearJEBackend(true, "userRoot", suffix);
    // Check that suffix is accessible while suffix2 is not.
    searchEntry(suffix, true);
    searchEntry(suffix2, false);
    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.
@@ -286,111 +213,48 @@
    addBaseEntry(suffix2, "networkgroup suffix");
    // Both old and new suffix should be accessible.
    searchEntry(suffix, true);
    searchEntry(suffix2, true);
    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, true);
    searchEntry(suffix2, false);
    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, false);
    searchEntry(suffix2, false);
    searchEntry(suffix, ResultCode.NO_SUCH_OBJECT);
    searchEntry(suffix2, ResultCode.NO_SUCH_OBJECT);
    // Add a base entry for the new suffix
    addBaseEntry(suffix2, "networkgroup suffix");
    // The new suffix is accessible while the old one is not.
    searchEntry(suffix, false);
    searchEntry(suffix2, true);
    searchEntry(suffix, ResultCode.NO_SUCH_OBJECT);
    searchEntry(suffix2, ResultCode.SUCCESS);
    // Reset the configuration with previous suffix
    modifyAttribute(backendConfigDN, ModificationType.REPLACE, backendBaseDNName, suffix);
  }
  /**
   * Tests that routing mode changes cause the network group config
   * manager to be initialized, shutdown, and reinitialized correctly.
   * <p>
   * Disabled because NGs are not supported (issue OPENDJ-335).
   *
   * @see <a
   *      href="https://opends.dev.java.net/issues/show_bug.cgi?id=3775">Issue 3775</a>
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test(enabled=false)
  public void testIssue3775() throws Exception
  {
    // Switch to and from manual mode twice in order to ensure that the
    // config manager is initialized twice. Then register a network
    // group. If the initialization has worked properly the network
    // group should be added successfully. In the case of issue 3775,
    // the config add listeners ended up being added twice so adding a
    // network group failed because the admin framework thought it had
    // been added twice.
    // Switch to manual mode once.
    TestCaseUtils.dsconfig(
        "set-global-configuration-prop",
        "--set", "workflow-configuration-mode:manual");
    try
    {
      // Switch back.
      TestCaseUtils.dsconfig(
          "set-global-configuration-prop",
          "--set", "workflow-configuration-mode:auto");
      // Switch to manual mode twice.
      TestCaseUtils.dsconfig(
          "set-global-configuration-prop",
          "--set", "workflow-configuration-mode:manual");
      // Now add network group.
      final String networkGroupID = "Network group issue 3775";
      TestCaseUtils.dsconfig(
          "create-network-group",
          "--group-name", networkGroupID,
          "--set", "enabled:true",
          "--set", "priority:" + 123);
      // Remove the network group.
      TestCaseUtils.dsconfig("delete-network-group", "--group-name", networkGroupID);
    }
    finally
    {
      TestCaseUtils.dsconfig(
          "set-global-configuration-prop",
          "--set", "workflow-configuration-mode:auto");
    }
  }
  /**
   * Tests the mechanism to attribute a network group to a client connection,
   * comparing the priority.
   * Create 2 network groups with different priorities.
   */
  @Test (dataProvider = "DNSet_4", groups = "virtual")
  public void testNetworkGroupPriority(
      String ng1,
      DN dn1,
      int prio1,
      String ng2,
      DN dn2,
      int prio2
      )
      throws Exception
  @Test(groups = "virtual")
  public void testNetworkGroupPriority() throws Exception
  {
    String ng1 = "group1";
    String ng2 = "group2";
    DN dn1 = DN.valueOf("o=test1");
    DN dn2 = DN.valueOf("o=test2");
    // Create and register the network group with the server.
    NetworkGroup networkGroup1 = new NetworkGroup(ng1);
    NetworkGroup networkGroup2 = new NetworkGroup(ng2);
@@ -410,8 +274,7 @@
   * subordinate naming context defined in the RootDSEBackend.
   */
  @Test
  public void testRootDseSubordinateNamingContext()
         throws Exception
  public void testRootDseSubordinateNamingContext() throws Exception
  {
    // Backends for the test
    String backend1   = "o=test-rootDSE-subordinate-naming-context-1";
@@ -419,22 +282,17 @@
    String backendID1 = "test-rootDSE-subordinate-naming-context-1";
    String backendID2 = "test-rootDSE-subordinate-naming-context-2";
    // Clean all the backends.
    TestCaseUtils.clearDataBackends();
    // Create a client connection for the test.
    InternalClientConnection connection =
      InternalClientConnection.getRootConnection();
    // 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(connection, true,  1);
    searchPublicNamingContexts(ResultCode.SUCCESS, 1);
    // Create another test backend and check that the new backend is visible
    TestCaseUtils.initializeMemoryBackend(backendID2, backend2, true);
    searchPublicNamingContexts(connection, true,  2);
    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
@@ -442,7 +300,7 @@
    TestCaseUtils.dsconfig(
        "set-root-dse-backend-prop",
        "--set", "subordinate-base-dn:" + backend1);
    searchPublicNamingContexts(connection, true, 1);
    searchPublicNamingContexts(ResultCode.SUCCESS, 1);
    // === Cleaning
@@ -451,34 +309,30 @@
    TestCaseUtils.dsconfig(
        "set-root-dse-backend-prop",
        "--reset", "subordinate-base-dn");
    searchPublicNamingContexts(connection, true, 2);
    searchPublicNamingContexts(ResultCode.SUCCESS, 2);
    // Clean the test backends. There is no more naming context.
    TestCaseUtils.clearMemoryBackend(backendID1);
    TestCaseUtils.clearMemoryBackend(backendID2);
    searchPublicNamingContexts(connection, false, 0);
    searchPublicNamingContexts(ResultCode.NO_SUCH_OBJECT, 0);
  }
  /**
   * Searches the list of naming contexts.
   *
   * @param connection    the connection to use for the search request
   * @param shouldExist   indicates whether at least one NC should be found
   * @param expectedRC  the expected result code
   * @param expectedNamingContexts  the number of expected naming contexts
   */
  private void searchPublicNamingContexts(
      InternalClientConnection connection,
      boolean shouldExist,
      int expectedNamingContexts
      ) throws Exception
  private void searchPublicNamingContexts(ResultCode expectedRC, int expectedNamingContexts) throws Exception
  {
    InternalClientConnection conn = InternalClientConnection.getRootConnection();
    SearchRequest request = newSearchRequest(DN.rootDN(), SearchScope.SINGLE_LEVEL);
    SearchOperation search = connection.processSearch(request);
    SearchOperation search = conn.processSearch(request);
    // Check the number of found naming context
    assertEquals(search.getResultCode(), shouldExist ? ResultCode.SUCCESS : ResultCode.NO_SUCH_OBJECT);
    if (shouldExist)
    assertEquals(search.getResultCode(), expectedRC);
    if (expectedRC == ResultCode.SUCCESS)
    {
      assertEquals(search.getEntriesSent(), expectedNamingContexts);
    }
@@ -488,16 +342,14 @@
  /**
   * Searches an entry on a given connection.
   *
   * @param baseDN        the request base DN string
   * @param shouldExist   if true the searched entry is expected to be found
   * @param baseDN the request base DN string
   * @param expectedRC the expected result code
   */
  private void searchEntry(String baseDN, boolean shouldExist) throws Exception
  private void searchEntry(String baseDN, ResultCode expectedRC) throws Exception
  {
    SearchRequest request = newSearchRequest(DN.valueOf(baseDN), SearchScope.BASE_OBJECT);
    SearchOperation search = getRootConnection().processSearch(request);
    // Compare the result code with the expected one
    assertEquals(search.getResultCode(), shouldExist ? ResultCode.SUCCESS : ResultCode.NO_SUCH_OBJECT);
    assertEquals(search.getResultCode(), expectedRC);
  }
@@ -532,8 +384,7 @@
      ) throws Exception
  {
    ArrayList<Modification> mods = new ArrayList<Modification>();
    Attribute attributeToModify =
      Attributes.create(attributeName, attributeValue);
    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);
@@ -543,7 +394,6 @@
  /**
   * Checks the DN routing through a network group.
   *
   * @param networkGroup    the network group to use for the check
   * @param dnToSearch      the DN of a workflow in the network group; may
   *                        be null
   * @param dnSubordinate   a subordinate of dnToSearch
@@ -552,11 +402,10 @@
   * @param shouldExist     true if we are supposed to find a workflow for
   *                        dnToSearch
   */
  private void doCheckNetworkGroup(
      NetworkGroup networkGroup,
  @Test (dataProvider = "DNSet_1", groups = "virtual")
  public void doCheckNetworkGroup(
      DN           dnToSearch,
      DN           dnSubordinate,
      DN           unrelatedDN,
      boolean      shouldExist
      )
  {
@@ -566,7 +415,7 @@
    }
    // Let's retrieve the workflow that maps best the dnToSearch
    Workflow workflow = networkGroup.getWorkflowCandidate(dnToSearch);
    Workflow workflow = NetworkGroup.getWorkflowCandidate(dnToSearch);
    if (shouldExist)
    {
      assertNotNull(workflow);
@@ -580,15 +429,9 @@
    // it should be the same than the one for dnToSearch
    if (dnSubordinate != null)
    {
       Workflow workflow2 = networkGroup.getWorkflowCandidate(dnSubordinate);
       Workflow workflow2 = NetworkGroup.getWorkflowCandidate(dnSubordinate);
       assertEquals(workflow2, workflow);
    }
    // Check that the unrelatedDN is not handled by any workflow
    if (unrelatedDN != null)
    {
      assertNull(networkGroup.getWorkflowCandidate(unrelatedDN));
    }
  }
}