From ba3f962a91112c98a32b2b70d51451baeeda23d5 Mon Sep 17 00:00:00 2001
From: jdemendi <jdemendi@localhost>
Date: Tue, 04 Nov 2008 09:53:59 +0000
Subject: [PATCH] fix 3560, Cannot recreate a workflow after a create/delete

---
 opendj-sdk/opends/src/server/org/opends/server/core/WorkflowConfigManager.java                   |  134 +++++++++++++++++++++-----
 opendj-sdk/opends/src/messages/messages/core.properties                                          |    6 +
 opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java                            |   61 +++++++++++
 opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java |   16 +++
 opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java              |   53 +++++++++-
 5 files changed, 234 insertions(+), 36 deletions(-)

diff --git a/opendj-sdk/opends/src/messages/messages/core.properties b/opendj-sdk/opends/src/messages/messages/core.properties
index c37eda3..008ca86 100644
--- a/opendj-sdk/opends/src/messages/messages/core.properties
+++ b/opendj-sdk/opends/src/messages/messages/core.properties
@@ -1807,4 +1807,10 @@
 SEVERE_ERR_REGISTER_WORKFLOW_BASE_DN_ALREADY_EXISTS_716=Unable to register \
  workflow node "%s" with the network group "%s" because another workflow node \
  "%s" with the same base DN "%s" is already registered
+INFO_ERR_WORKFLOW_IN_USE_717=\
+Unable to remove the workflow "%s" because the workflow is still in use \
+(there are still %d reference(s) to the workflow)
+INFO_ERR_WORKFLOW_DOES_NOT_EXIST_718=\
+Unable to register the workflow "%s" with the network group %s because \
+the workflow does not exist
  
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowConfigManager.java b/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowConfigManager.java
index a0d79a6..0b30de4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowConfigManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowConfigManager.java
@@ -28,6 +28,8 @@
 
 
 
+import static org.opends.messages.CoreMessages.*;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -143,9 +145,9 @@
   public ConfigChangeResult applyConfigurationAdd(
       WorkflowCfg configuration)
   {
-    ResultCode         resultCode          = ResultCode.SUCCESS;
-    boolean            adminActionRequired = false;
-    ArrayList<Message> messages            = new ArrayList<Message>();
+    ResultCode    resultCode          = ResultCode.SUCCESS;
+    boolean       adminActionRequired = false;
+    List<Message> messages            = new ArrayList<Message>();
 
     configuration.addChangeListener(this);
 
@@ -160,7 +162,7 @@
       {
         if (resultCode == ResultCode.SUCCESS)
         {
-          resultCode = DirectoryServer.getServerErrorResultCode();
+          resultCode = de.getResultCode();
         }
 
         messages.add(de.getMessageObject());
@@ -179,7 +181,16 @@
       WorkflowCfg   configuration,
       List<Message> unacceptableReasons)
   {
-    return true;
+    boolean acceptable = true;
+    WorkflowImpl existingWorkflow = workflows.get(configuration.dn());
+    if (existingWorkflow != null)
+    {
+      // check whether we can delete the workflow
+      acceptable = checkReferenceCounter(
+        existingWorkflow, unacceptableReasons);
+    }
+
+    return acceptable;
   }
 
 
@@ -190,16 +201,33 @@
   public ConfigChangeResult applyConfigurationDelete(
       WorkflowCfg configuration)
   {
-    ResultCode         resultCode          = ResultCode.SUCCESS;
-    boolean            adminActionRequired = false;
-    ArrayList<Message> messages            = new ArrayList<Message>();
+    ResultCode    resultCode          = ResultCode.SUCCESS;
+    boolean       adminActionRequired = false;
+    List<Message> messages            = new ArrayList<Message>();
 
-
+    // check first whether we can remove the workflow
     WorkflowImpl workflow = workflows.remove(configuration.dn());
     if (workflow != null)
     {
-      workflow.deregister();
-      workflow.finalizeWorkflow();
+      boolean acceptable = checkReferenceCounter(workflow, messages);
+      if (acceptable)
+      {
+        // The workflow is not used anymore, we can remove it
+        workflow.deregister();
+        workflow.finalizeWorkflow();
+
+        // Deregister the workflow with the internal network group
+        NetworkGroup.getInternalNetworkGroup().deregisterWorkflow(
+          workflow.getWorkflowId());
+
+        // Deregister the workflow with the admin network group
+        NetworkGroup.getAdminNetworkGroup().deregisterWorkflow(
+          workflow.getWorkflowId());
+      }
+      else
+      {
+        resultCode = ResultCode.UNWILLING_TO_PERFORM;
+      }
     }
 
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
@@ -214,8 +242,19 @@
       WorkflowCfg   configuration,
       List<Message> unacceptableReasons)
   {
-    // Nothing to check.
-    return true;
+    // Get the existing workflow if it's already enabled.
+    WorkflowImpl existingWorkflow = workflows.get(configuration.dn());
+
+    // Is this a request to disable the workflow?
+    boolean acceptable = true;
+    if (! configuration.isEnabled() && (existingWorkflow != null))
+    {
+      // check whether we can disable the workflow
+      acceptable = checkReferenceCounter(
+        existingWorkflow, unacceptableReasons);
+    }
+
+    return acceptable;
   }
 
 
@@ -226,13 +265,9 @@
   public ConfigChangeResult applyConfigurationChange(
       WorkflowCfg configuration)
   {
-    ResultCode         resultCode          = ResultCode.SUCCESS;
-    boolean            adminActionRequired = false;
-    ArrayList<Message> messages            = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-      new ConfigChangeResult(resultCode, adminActionRequired, messages);
-
+    ResultCode    resultCode          = ResultCode.SUCCESS;
+    boolean       adminActionRequired = false;
+    List<Message> messages            = new ArrayList<Message>();
 
     // Get the existing workflow if it's already enabled.
     WorkflowImpl existingWorkflow = workflows.get(configuration.dn());
@@ -243,12 +278,30 @@
     {
       if (existingWorkflow != null)
       {
-        workflows.remove(configuration.dn());
-        existingWorkflow.deregister();
-        existingWorkflow.finalizeWorkflow();
+        // check whether we can disable the workflow
+        boolean acceptable = checkReferenceCounter(existingWorkflow, messages);
+        if (acceptable)
+        {
+          // The workflow is not used anymore, we can remove it
+          workflows.remove(configuration.dn());
+          existingWorkflow.deregister();
+          existingWorkflow.finalizeWorkflow();
+
+          // Deregister the workflow with the internal network group
+          NetworkGroup.getInternalNetworkGroup().deregisterWorkflow(
+            existingWorkflow.getWorkflowId());
+
+          // Deregister the workflow with the admin network group
+          NetworkGroup.getAdminNetworkGroup().deregisterWorkflow(
+            existingWorkflow.getWorkflowId());
+        }
+        else
+        {
+          resultCode = ResultCode.UNWILLING_TO_PERFORM;
+        }
       }
 
-      return configChangeResult;
+      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
     }
 
     // If the workflow is disabled then create and register it.
@@ -262,7 +315,7 @@
       {
         if (resultCode == ResultCode.SUCCESS)
         {
-          resultCode = DirectoryServer.getServerErrorResultCode();
+          resultCode = de.getResultCode();
         }
 
         messages.add(de.getMessageObject());
@@ -274,7 +327,7 @@
       existingWorkflow.updateConfig(configuration);
     }
 
-    return configChangeResult;
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
 
 
@@ -319,5 +372,34 @@
     NetworkGroup.getAdminNetworkGroup().registerWorkflow(workflowImpl);
   }
 
+
+  /**
+   * Checks whether a workflow is no more used so that we can delete
+   * or disable it.
+   *
+   * @param workflow  the workflow to check
+   * @param messages  a list of reasons that prevent the workflow to be
+   *                  deleted or disabled
+   * @return <code>true</code> when the workflow can be deleted or disabled
+   */
+  private boolean checkReferenceCounter(
+      WorkflowImpl  workflow,
+      List<Message> messages
+      )
+  {
+    boolean acceptable = true;
+
+    int refCounter = workflow.getReferenceCounter();
+    if (refCounter != 0)
+    {
+      Message message = INFO_ERR_WORKFLOW_IN_USE.get(
+        workflow.getWorkflowId(), workflow.getReferenceCounter());
+      messages.add(message);
+      acceptable = false;
+    }
+
+    return acceptable;
+  }
+
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java b/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java
index ca8ce53..6193e3f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/WorkflowImpl.java
@@ -34,7 +34,6 @@
 import java.util.Observer;
 import java.util.TreeMap;
 
-import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 import org.opends.server.admin.std.server.WorkflowCfg;
 import org.opends.server.types.*;
@@ -85,6 +84,12 @@
   // A lock to protect concurrent access to the registeredWorkflows.
   private static Object registeredWorkflowsLock = new Object();
 
+  // A reference counter used to count the number of workflow nodes that
+  // were registered with a network group. A workflow can be disabled or
+  // deleted only when its reference counter value is 0.
+  private int referenceCounter = 0;
+  private Object referenceCounterLock = new Object();
+
 
   /**
    * Creates a new instance of a workflow implementation. To define a workflow
@@ -219,10 +224,9 @@
       // The workflow must not be already registered
       if (registeredWorkflows.containsKey(workflowID))
       {
-        Message message =
-            ERR_REGISTER_WORKFLOW_ALREADY_EXISTS.get(workflowID);
         throw new DirectoryException(
-            ResultCode.UNWILLING_TO_PERFORM, message);
+            ResultCode.UNWILLING_TO_PERFORM,
+            ERR_REGISTER_WORKFLOW_ALREADY_EXISTS.get(workflowID));
       }
 
       TreeMap<String, Workflow> newRegisteredWorkflows =
@@ -466,4 +470,53 @@
       rootWorkflowElement = null;
     }
   }
+
+
+  /**
+   * Increments the workflow reference counter.
+   * <p>
+   * As long as the counter value is not 0 the workflow cannot be
+   * disabled nor deleted.
+   */
+  public void incrementReferenceCounter()
+  {
+    synchronized (referenceCounterLock)
+    {
+      referenceCounter++;
+    }
+  }
+
+
+  /**
+   * Decrements the workflow reference counter.
+   * <p>
+   * As long as the counter value is not 0 the workflow cannot be
+   * disabled nor deleted.
+   */
+  public void decrementReferenceCounter()
+  {
+    synchronized (referenceCounterLock)
+    {
+      if (referenceCounter == 0)
+      {
+        // the counter value is 0, we should not need to decrement anymore
+        throw new AssertionError(
+          "Reference counter of the workflow " + workflowID
+          + " is already set to 0, cannot decrement it anymore"
+          );
+      }
+      referenceCounter--;
+    }
+  }
+
+
+  /**
+   * Gets the value of the reference counter of the workflow.
+   *
+   * @return the reference counter of the workflow
+   */
+  public int getReferenceCounter()
+  {
+    return referenceCounter;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java b/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
index 3fcb443..59b119f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
@@ -80,26 +80,28 @@
   // access to all the workflows. The purpose of the default network
   // group is to allow new clients to perform a first operation before
   // they can be attached to a specific network group.
+  private static final String DEFAULT_NETWORK_GROUP_NAME = "default";
+  private final boolean isDefaultNetworkGroup;
   private static NetworkGroup defaultNetworkGroup =
-      new NetworkGroup ("default");
+      new NetworkGroup (DEFAULT_NETWORK_GROUP_NAME);
 
 
   // The admin network group (singleton).
   // The admin network group has no criterion, no policy, and gives
   // access to all the workflows.
   private static final String ADMIN_NETWORK_GROUP_NAME = "admin";
+  private final boolean isAdminNetworkGroup;
   private static NetworkGroup adminNetworkGroup =
       new NetworkGroup (ADMIN_NETWORK_GROUP_NAME);
-  private final boolean isAdminNetworkGroup;
 
   // The internal network group (singleton).
   // The internal network group has no criterion, no policy, and gives
   // access to all the workflows. The purpose of the internal network
   // group is to allow internal connections to perform operations.
   private static final String INTERNAL_NETWORK_GROUP_NAME = "internal";
+  private boolean isInternalNetworkGroup;
   private static NetworkGroup internalNetworkGroup =
       new NetworkGroup(INTERNAL_NETWORK_GROUP_NAME);
-  private boolean isInternalNetworkGroup;
 
 
   // The list of all network groups that are registered with the server.
@@ -142,7 +144,8 @@
     this.networkGroupID = networkGroupID;
 
     isInternalNetworkGroup = INTERNAL_NETWORK_GROUP_NAME.equals(networkGroupID);
-    isAdminNetworkGroup = ADMIN_NETWORK_GROUP_NAME.equals(networkGroupID);
+    isAdminNetworkGroup    = ADMIN_NETWORK_GROUP_NAME.equals(networkGroupID);
+    isDefaultNetworkGroup  = DEFAULT_NETWORK_GROUP_NAME.equals(networkGroupID);
   }
 
 
@@ -302,6 +305,17 @@
 
       // 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.
+      if (!isAdminNetworkGroup
+          && !isInternalNetworkGroup
+          && !isDefaultNetworkGroup)
+      {
+        workflow.incrementReferenceCounter();
+      }
     }
   }
 
@@ -354,6 +368,17 @@
       }
     }
 
+    // Now that the workflow node has been deregistered with the network
+    // group, update the reference counter of the workflow.
+    if ((workflow != null)
+        && !isAdminNetworkGroup
+        && !isInternalNetworkGroup
+        && !isDefaultNetworkGroup)
+    {
+      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
+      workflowImpl.decrementReferenceCounter();
+    }
+
     return workflow;
   }
 
@@ -363,11 +388,14 @@
    * deregister is identified by its workflow ID.
    *
    * @param workflowID the workflow identifier of the workflow to deregister
+   * @return the deregistered workflow
    */
-  public void deregisterWorkflow(
+  public Workflow deregisterWorkflow(
       String workflowID
       )
   {
+    Workflow workflow = null;
+
     String rootDSEWorkflowID = null;
     if (rootDSEWorkflowNode != null)
     {
@@ -378,6 +406,7 @@
     {
       // deregister the rootDSE
       deregisterWorkflow(rootDSEWorkflowNode);
+      workflow = rootDSEWorkflowNode.getWorkflowImpl();
     }
     else
     {
@@ -392,6 +421,7 @@
             // Call deregisterWorkflow() instead of deregisterWorkflowNode()
             // because we want the naming context list to be updated as well.
             deregisterWorkflow(node);
+            workflow = node.getWorkflowImpl();
 
             // Only one workflow can match the baseDN, so we can break
             // the loop here.
@@ -400,6 +430,19 @@
         }
       }
     }
+
+    // Now that the workflow node has been deregistered with the network
+    // group, update the reference counter of the workflow.
+    if ((workflow != null)
+        && !isAdminNetworkGroup
+        && !isInternalNetworkGroup
+        && !isDefaultNetworkGroup)
+    {
+      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
+      workflowImpl.decrementReferenceCounter();
+    }
+
+    return workflow;
   }
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java b/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java
index 40df1d2..35d4276 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java
@@ -28,6 +28,9 @@
 
 
 
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.SortedSet;
@@ -332,7 +335,18 @@
     {
       WorkflowImpl workflowImpl =
         (WorkflowImpl) WorkflowImpl.getWorkflow(workflowID);
-      networkGroup.registerWorkflow(workflowImpl);
+      if (workflowImpl == null)
+      {
+        // The workflow does not exist, log an error message
+        // and skip the workflow
+        Message message = INFO_ERR_WORKFLOW_DOES_NOT_EXIST.get(
+          workflowID, networkGroupId);
+        logError(message);
+      }
+      else
+      {
+        networkGroup.registerWorkflow(workflowImpl);
+      }
     }
 
     // register the root DSE workflow with the network group

--
Gitblit v1.10.0