From 218b40d6e175f5b58b89ff7e0b3050577d3aff2f Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 10 Nov 2008 13:41:49 +0000
Subject: [PATCH] This change fixes issue 3567:

---
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java                    |   84 +-
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java                    |   79 ++
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java                      |    9 
 opends/src/server/org/opends/server/core/AddOperationBasis.java                                                      |   64 --
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java                  |   64 +
 opends/src/server/org/opends/server/types/AbstractOperation.java                                                     |   47 +
 opends/src/server/org/opends/server/core/PersistentSearch.java                                                       |  630 +++++++++++++-----------
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java                   |   15 
 opends/src/server/org/opends/server/core/DeleteOperationBasis.java                                                   |   66 --
 opends/src/server/org/opends/server/core/ModifyDNOperationBasis.java                                                 |   67 --
 opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java                                           |    6 
 opends/src/server/org/opends/server/core/ModifyOperationBasis.java                                                   |   68 --
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java                       |   62 +
 opends/src/server/org/opends/server/core/DirectoryServer.java                                                        |   55 --
 opends/src/server/org/opends/server/core/SearchOperationBasis.java                                                   |    2 
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java                    |   25 
 opends/src/server/org/opends/server/api/ClientConnection.java                                                        |   15 
 opends/src/server/org/opends/server/core/OperationWrapper.java                                                       |    8 
 opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java |    4 
 opends/src/server/org/opends/server/types/Operation.java                                                             |   10 
 opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java                                 |    8 
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java                    |   63 +
 opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java                                         |   29 
 opends/src/server/org/opends/server/core/AbandonOperationBasis.java                                                  |    9 
 24 files changed, 706 insertions(+), 783 deletions(-)

diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index c56d565..e2be916 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -32,6 +32,7 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.Selector;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -45,7 +46,6 @@
 import org.opends.server.core.SearchOperation;
 import org.opends.server.core.networkgroups.NetworkGroup;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.types.AbstractOperation;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
@@ -814,8 +814,7 @@
    * @return  The set of operations in progress for this client
    *          connection.
    */
-  public abstract Collection<AbstractOperation>
-                                      getOperationsInProgress();
+  public abstract Collection<Operation> getOperationsInProgress();
 
 
 
@@ -828,8 +827,7 @@
    * @return  The operation in progress with the specified message ID,
    *          or {@code null} if no such operation could be found.
    */
-  public abstract AbstractOperation
-                          getOperationInProgress(int messageID);
+  public abstract Operation getOperationInProgress(int messageID);
 
 
 
@@ -857,8 +855,7 @@
    * @return  The set of persistent searches registered for this
    *          client.
    */
-  public final CopyOnWriteArrayList<PersistentSearch>
-                    getPersistentSearches()
+  public final List<PersistentSearch> getPersistentSearches()
   {
     return persistentSearches;
   }
@@ -1660,13 +1657,13 @@
 
     if ((authzDN == null) || authzDN.isNullDN())
     {
-      return java.util.Collections.<Group>emptySet();
+      return Collections.<Group>emptySet();
     }
 
     Entry userEntry = DirectoryServer.getEntry(authzDN);
     if (userEntry == null)
     {
-      return java.util.Collections.<Group>emptySet();
+      return Collections.<Group>emptySet();
     }
 
     HashSet<Group> groupSet = new HashSet<Group>();
diff --git a/opends/src/server/org/opends/server/core/AbandonOperationBasis.java b/opends/src/server/org/opends/server/core/AbandonOperationBasis.java
index 649f815..b3e48f0 100644
--- a/opends/src/server/org/opends/server/core/AbandonOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/AbandonOperationBasis.java
@@ -39,8 +39,6 @@
 
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.plugin.PluginResult;
-import org.opends.server.loggers.debug.DebugLogger;
-import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.*;
 import org.opends.server.types.operation.PostOperationAbandonOperation;
 import org.opends.server.types.operation.PreParseAbandonOperation;
@@ -57,11 +55,6 @@
                PostOperationAbandonOperation
 {
 
-  /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = DebugLogger.getTracer();
-
   // The message ID of the operation that should be abandoned.
   private final int idToAbandon;
 
@@ -251,7 +244,7 @@
       // code to reflect whether the abandon was successful and an error message
       // if it was not.  Even though there is no response, the result should
       // still be logged.
-      AbstractOperation operation =
+      Operation operation =
            clientConnection.getOperationInProgress(idToAbandon);
       if (operation == null)
       {
diff --git a/opends/src/server/org/opends/server/core/AddOperationBasis.java b/opends/src/server/org/opends/server/core/AddOperationBasis.java
index 4d74a49..7280815 100644
--- a/opends/src/server/org/opends/server/core/AddOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/AddOperationBasis.java
@@ -25,7 +25,6 @@
  *      Copyright 2007-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
-import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 
 
@@ -38,10 +37,8 @@
 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE;
 import static org.opends.server.loggers.AccessLogger.logAddRequest;
 import static org.opends.server.loggers.AccessLogger.logAddResponse;
-import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.messages.CoreMessages.*;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
 import static org.opends.server.util.StaticUtils.toLowerCase;
 
 import java.util.ArrayList;
@@ -797,9 +794,10 @@
       // Log the add response message.
       logAddResponse(this);
 
-      // Notifies any persistent searches that might be registered with the
-      // server.
-      notifyPersistentSearches(workflowExecuted);
+      // Invoke the post-response callbacks.
+      if (workflowExecuted) {
+        invokePostResponseCallbacks();
+      }
 
       // Invoke the post-response add plugins.
       invokePostResponsePlugins(workflowExecuted);
@@ -855,60 +853,6 @@
   }
 
 
-  /**
-   * Notifies any persistent searches that might be registered with the server.
-   * If no workflow has been executed then don't notify persistent searches.
-   *
-   * @param workflowExecuted <code>true</code> if a workflow has been
-   *                         executed
-   */
-  private void notifyPersistentSearches(boolean workflowExecuted)
-  {
-    if (! workflowExecuted)
-    {
-      return;
-    }
-
-    List<?> localOperations =
-      (List<?>)getAttachment(Operation.LOCALBACKENDOPERATIONS);
-
-    if (localOperations != null)
-    {
-      for (Object localOp : localOperations)
-      {
-        LocalBackendAddOperation localOperation =
-          (LocalBackendAddOperation)localOp;
-
-        if ((getResultCode() == ResultCode.SUCCESS) &&
-            (localOperation.getEntryToAdd() != null))
-        {
-          for (PersistentSearch persistentSearch :
-            DirectoryServer.getPersistentSearches())
-          {
-            try
-            {
-              persistentSearch.processAdd(localOperation,
-                  localOperation.getEntryToAdd());
-            }
-            catch (Exception e)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-
-              Message message = ERR_ADD_ERROR_NOTIFYING_PERSISTENT_SEARCH.get(
-                  String.valueOf(persistentSearch), getExceptionMessage(e));
-              logError(message);
-
-              DirectoryServer.deregisterPersistentSearch(persistentSearch);
-            }
-          }
-        }
-      }
-    }
-  }
-
 
   /**
    * Updates the error message and the result code of the operation.
diff --git a/opends/src/server/org/opends/server/core/DeleteOperationBasis.java b/opends/src/server/org/opends/server/core/DeleteOperationBasis.java
index 3d33ca6..2bbd45b 100644
--- a/opends/src/server/org/opends/server/core/DeleteOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/DeleteOperationBasis.java
@@ -25,7 +25,6 @@
  *      Copyright 2007-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
-import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 
 
@@ -37,11 +36,8 @@
 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE;
 import static org.opends.server.loggers.AccessLogger.logDeleteRequest;
 import static org.opends.server.loggers.AccessLogger.logDeleteResponse;
-import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.messages.CoreMessages.*;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
-
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -452,9 +448,10 @@
       // Log the delete response.
       logDeleteResponse(this);
 
-      // Notifies any persistent searches that might be registered with the
-      // server.
-      notifyPersistentSearches(workflowExecuted);
+      // Invoke the post-response callbacks.
+      if (workflowExecuted) {
+        invokePostResponseCallbacks();
+      }
 
       // Invoke the post-response delete plugins.
       invokePostResponsePlugins(workflowExecuted);
@@ -511,61 +508,6 @@
 
 
   /**
-   * Notifies any persistent searches that might be registered with the server.
-   * If no workflow has been executed then don't notify persistent searches.
-   *
-   * @param workflowExecuted <code>true</code> if a workflow has been
-   *                         executed
-   */
-  private void notifyPersistentSearches(boolean workflowExecuted)
-  {
-    if (! workflowExecuted)
-    {
-      return;
-    }
-
-    List localOperations =
-      (List)getAttachment(Operation.LOCALBACKENDOPERATIONS);
-
-    if (localOperations != null)
-    {
-      for (Object localOp : localOperations)
-      {
-        LocalBackendDeleteOperation localOperation =
-          (LocalBackendDeleteOperation)localOp;
-        // Notify any persistent searches that might be registered with the
-        // server.
-        if (getResultCode() == ResultCode.SUCCESS)
-        {
-          for (PersistentSearch persistentSearch :
-            DirectoryServer.getPersistentSearches())
-          {
-            try
-            {
-              persistentSearch.processDelete(localOperation,
-                  localOperation.getEntryToDelete());
-            }
-            catch (Exception e)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-
-              Message message = ERR_DELETE_ERROR_NOTIFYING_PERSISTENT_SEARCH.
-                  get(String.valueOf(persistentSearch), getExceptionMessage(e));
-              logError(message);
-
-              DirectoryServer.deregisterPersistentSearch(persistentSearch);
-            }
-          }
-        }
-      }
-    }
-  }
-
-
-  /**
    * Updates the error message and the result code of the operation.
    *
    * This method is called because no workflows were found to process
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 6f5b117..ebe41bf 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -497,9 +497,6 @@
   // The set of import task listeners registered with the Directory Server.
   private CopyOnWriteArrayList<ImportTaskListener> importTaskListeners;
 
-  // The set of persistent searches registered with the Directory Server.
-  private CopyOnWriteArrayList<PersistentSearch> persistentSearches;
-
   // The set of restore task listeners registered with the Directory Server.
   private CopyOnWriteArrayList<RestoreTaskListener> restoreTaskListeners;
 
@@ -937,8 +934,6 @@
       directoryServer.baseDnRegistry = new BaseDnRegistry();
       directoryServer.changeNotificationListeners =
            new CopyOnWriteArrayList<ChangeNotificationListener>();
-      directoryServer.persistentSearches =
-           new CopyOnWriteArrayList<PersistentSearch>();
       directoryServer.shutdownListeners =
            new CopyOnWriteArrayList<ServerShutdownListener>();
       directoryServer.synchronizationProviders =
@@ -7730,56 +7725,6 @@
 
 
   /**
-   * Retrieves the set of persistent searches registered with the Directory
-   * Server.
-   *
-   * @return  The set of persistent searches registered with the Directory
-   *          Server.
-   */
-  public static CopyOnWriteArrayList<PersistentSearch> getPersistentSearches()
-  {
-    return directoryServer.persistentSearches;
-  }
-
-
-
-  /**
-   * Registers the provided persistent search operation with the Directory
-   * Server so that it will be notified of any add, delete, modify, or modify DN
-   * operations that are performed.
-   *
-   * @param  persistentSearch  The persistent search operation to register with
-   *                           the Directory Server.
-   */
-  public static void registerPersistentSearch(PersistentSearch persistentSearch)
-  {
-    directoryServer.persistentSearches.add(persistentSearch);
-    persistentSearch.getSearchOperation().getClientConnection().
-         registerPersistentSearch(persistentSearch);
-  }
-
-
-
-  /**
-   * Deregisters the provided persistent search operation with the Directory
-   * Server so that it will no longer be notified of any add, delete, modify, or
-   * modify DN operations that are performed.
-   *
-   * @param  persistentSearch  The persistent search operation to deregister
-   *                           with the Directory Server.
-   */
-  public static void deregisterPersistentSearch(PersistentSearch
-                                                     persistentSearch)
-  {
-    directoryServer.persistentSearches.remove(persistentSearch);
-    persistentSearch.getSearchOperation().getClientConnection().
-         deregisterPersistentSearch(persistentSearch);
-  }
-
-
-
-
-  /**
    * Retrieves the set of synchronization providers that have been registered
    * with the Directory Server.
    *
diff --git a/opends/src/server/org/opends/server/core/ModifyDNOperationBasis.java b/opends/src/server/org/opends/server/core/ModifyDNOperationBasis.java
index ef02509..3f1643c 100644
--- a/opends/src/server/org/opends/server/core/ModifyDNOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/ModifyDNOperationBasis.java
@@ -25,7 +25,6 @@
  *      Copyright 2006-2008 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
-import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 
 import java.util.ArrayList;
@@ -47,9 +46,7 @@
 
 import org.opends.server.loggers.debug.DebugLogger;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.loggers.ErrorLogger;
 import static org.opends.messages.CoreMessages.*;
-import static org.opends.server.util.StaticUtils.*;
 
 
 /**
@@ -665,9 +662,10 @@
       // Log the modify DN response.
       logModifyDNResponse(this);
 
-      // Notifies any persistent searches that might be registered with the
-      // server.
-      notifyPersistentSearches(workflowExecuted);
+      // Invoke the post-response callbacks.
+      if (workflowExecuted) {
+        invokePostResponseCallbacks();
+      }
 
       // Invoke the post-response modify DN plugins.
       invokePostResponsePlugins(workflowExecuted);
@@ -724,63 +722,6 @@
 
 
   /**
-   * Notifies any persistent searches that might be registered with the server.
-   * If no workflow has been executed then don't notify persistent searches.
-   *
-   * @param workflowExecuted <code>true</code> if a workflow has been
-   *                         executed
-   */
-  private void notifyPersistentSearches(boolean workflowExecuted)
-  {
-    if (! workflowExecuted)
-    {
-      return;
-    }
-
-    List localOperations =
-      (List)getAttachment(Operation.LOCALBACKENDOPERATIONS);
-
-    if (localOperations != null)
-    {
-      for (Object localOperation : localOperations)
-      {
-        LocalBackendModifyDNOperation localOp =
-          (LocalBackendModifyDNOperation)localOperation;
-        // Notify any persistent searches that might be registered with
-        // the server.
-        if (getResultCode() == ResultCode.SUCCESS)
-        {
-          for (PersistentSearch persistentSearch :
-            DirectoryServer.getPersistentSearches())
-          {
-            try
-            {
-              persistentSearch.processModifyDN(
-                  localOp,
-                  localOp.getOriginalEntry(),
-                  localOp.getUpdatedEntry());
-            }
-            catch (Exception e)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-
-              Message message = ERR_MODDN_ERROR_NOTIFYING_PERSISTENT_SEARCH.get(
-                  String.valueOf(persistentSearch), getExceptionMessage(e));
-              ErrorLogger.logError(message);
-
-              DirectoryServer.deregisterPersistentSearch(persistentSearch);
-            }
-          }
-        }
-      }
-    }
-  }
-
-
-  /**
    * Updates the error message and the result code of the operation.
    *
    * This method is called because no workflows were found to process
diff --git a/opends/src/server/org/opends/server/core/ModifyOperationBasis.java b/opends/src/server/org/opends/server/core/ModifyOperationBasis.java
index 8ab08ce..43474bd 100644
--- a/opends/src/server/org/opends/server/core/ModifyOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/ModifyOperationBasis.java
@@ -27,9 +27,6 @@
 package org.opends.server.core;
 
 import org.opends.messages.MessageBuilder;
-import org.opends.messages.Message;
-
-
 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ENTRY_DN;
 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE;
 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_MATCHED_DN;
@@ -38,11 +35,8 @@
 import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE;
 import static org.opends.server.loggers.AccessLogger.logModifyRequest;
 import static org.opends.server.loggers.AccessLogger.logModifyResponse;
-import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.messages.CoreMessages.*;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
-
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -554,9 +548,10 @@
       // Log the modify response.
       logModifyResponse(this);
 
-      // Notifies any persistent searches that might be registered with the
-      // server.
-      notifyPersistentSearches(workflowExecuted);
+      // Invoke the post-response callbacks.
+      if (workflowExecuted) {
+        invokePostResponseCallbacks();
+      }
 
       // Invoke the post-response add plugins.
       invokePostResponsePlugins(workflowExecuted);
@@ -612,61 +607,6 @@
   }
 
 
-  /**
-   * Notifies any persistent searches that might be registered with the server.
-   * If no workflow has been executed then don't notify persistent searches.
-   *
-   * @param workflowExecuted <code>true</code> if a workflow has been
-   *                         executed
-   */
-  private void notifyPersistentSearches(boolean workflowExecuted)
-  {
-    if (! workflowExecuted)
-    {
-      return;
-    }
-
-    List localOperations =
-      (List)getAttachment(Operation.LOCALBACKENDOPERATIONS);
-
-    if (localOperations != null)
-    {
-      for (Object localOp : localOperations)
-      {
-        LocalBackendModifyOperation localOperation =
-          (LocalBackendModifyOperation)localOp;
-        // Notify any persistent searches that might be registered with
-        // the server.
-        if (localOperation.getResultCode() == ResultCode.SUCCESS)
-        {
-          for (PersistentSearch persistentSearch :
-               DirectoryServer.getPersistentSearches())
-          {
-            try
-            {
-              persistentSearch.processModify(localOperation,
-                  localOperation.getCurrentEntry(),
-                  localOperation.getModifiedEntry());
-            }
-            catch (Exception e)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-
-              Message message = ERR_MODIFY_ERROR_NOTIFYING_PERSISTENT_SEARCH.
-                  get(String.valueOf(persistentSearch), getExceptionMessage(e));
-              logError(message);
-
-              DirectoryServer.deregisterPersistentSearch(persistentSearch);
-            }
-          }
-        }
-      }
-    }
-  }
-
 
   /**
    * Updates the error message and the result code of the operation.
diff --git a/opends/src/server/org/opends/server/core/OperationWrapper.java b/opends/src/server/org/opends/server/core/OperationWrapper.java
index 5027094..02684d7 100644
--- a/opends/src/server/org/opends/server/core/OperationWrapper.java
+++ b/opends/src/server/org/opends/server/core/OperationWrapper.java
@@ -484,5 +484,13 @@
       throws CanceledOperationException {
     operation.checkIfCanceled(signalTooLate);
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void registerPostResponseCallback(Runnable callback)
+  {
+    operation.registerPostResponseCallback(callback);
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/core/PersistentSearch.java b/opends/src/server/org/opends/server/core/PersistentSearch.java
index 5e33329..a40bd0f 100644
--- a/opends/src/server/org/opends/server/core/PersistentSearch.java
+++ b/opends/src/server/org/opends/server/core/PersistentSearch.java
@@ -27,206 +27,219 @@
 package org.opends.server.core;
 
 
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.opends.server.controls.EntryChangeNotificationControl;
 import org.opends.server.controls.PersistentSearchChangeType;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.CancelResult;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
 import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
 import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SearchScope;
-import org.opends.server.workflowelement.localbackend.*;
-
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
 
 
 
 /**
- * This class defines a data structure that will be used to hold the information
- * necessary for processing a persistent search.
+ * This class defines a data structure that will be used to hold the
+ * information necessary for processing a persistent search.
  */
-public class PersistentSearch
+public final class PersistentSearch
 {
+
+  /**
+   * A cancellation call-back which can be used by work-flow element
+   * implementations in order to register for resource cleanup when a
+   * persistent search is cancelled.
+   */
+  public static interface CancellationCallback
+  {
+
+    /**
+     * The provided persistent search has been cancelled. Any
+     * resources associated with the persistent search should be
+     * released.
+     *
+     * @param psearch
+     *          The persistent search which has just been cancelled.
+     */
+    void persistentSearchCancelled(PersistentSearch psearch);
+  }
+
   /**
    * The tracer object for the debug logger.
    */
   private static final DebugTracer TRACER = getTracer();
 
-  // Indicates whether entries returned should include the entry change
-  // notification control.
-  private boolean returnECs;
+  // Indicates whether entries returned should include the entry
+  // change notification control.
+  private final boolean returnECs;
 
   // The base DN for the search operation.
-  private DN baseDN;
+  private final DN baseDN;
 
   // The set of change types we want to see.
-  private Set<PersistentSearchChangeType> changeTypes;
+  private final Set<PersistentSearchChangeType> changeTypes;
 
   // The scope for the search operation.
-  private SearchScope scope;
+  private final SearchScope scope;
 
   // The filter for the search operation.
-  private SearchFilter filter;
+  private final SearchFilter filter;
 
   // The reference to the associated search operation.
-  private SearchOperation searchOperation;
+  private final SearchOperation searchOperation;
+
+  // Indicates whether or not this persistent search has already been
+  // aborted.
+  private boolean isCancelled = false;
+
+  // Cancellation callbacks which should be run when this persistent
+  // search is cancelled.
+  private final List<CancellationCallback> cancellationCallbacks =
+    new CopyOnWriteArrayList<CancellationCallback>();
 
 
 
   /**
-   * Creates a new persistent search object with the provided information.
+   * Creates a new persistent search object with the provided
+   * information.
    *
-   * @param  searchOperation  The search operation for this persistent search.
-   * @param  changeTypes      The change types for which changes should be
-   *                          examined.
-   * @param  returnECs        Indicates whether to include entry change
-   *                          notification controls in search result entries
-   *                          sent to the client.
+   * @param searchOperation
+   *          The search operation for this persistent search.
+   * @param changeTypes
+   *          The change types for which changes should be examined.
+   * @param returnECs
+   *          Indicates whether to include entry change notification
+   *          controls in search result entries sent to the client.
    */
   public PersistentSearch(SearchOperation searchOperation,
-                          Set<PersistentSearchChangeType> changeTypes,
-                          boolean returnECs)
+      Set<PersistentSearchChangeType> changeTypes, boolean returnECs)
   {
     this.searchOperation = searchOperation;
-    this.changeTypes     = changeTypes;
-    this.returnECs       = returnECs;
+    this.changeTypes = changeTypes;
+    this.returnECs = returnECs;
 
-    baseDN = searchOperation.getBaseDN();
-    scope  = searchOperation.getScope();
-    filter = searchOperation.getFilter();
+    this.baseDN = searchOperation.getBaseDN();
+    this.scope = searchOperation.getScope();
+    this.filter = searchOperation.getFilter();
   }
 
 
 
   /**
-   * Retrieves the search operation for this persistent search.
+   * Cancels this persistent search operation. On exit this persistent
+   * search will no longer be valid and any resources associated with
+   * it will have been released.
    *
-   * @return  The search operation for this persistent search.
+   * @return The result of the cancellation.
    */
-  public SearchOperation getSearchOperation()
+  public synchronized CancelResult cancel()
   {
-    return searchOperation;
+    if (!isCancelled)
+    {
+      isCancelled = true;
+
+      // The persistent search can no longer be cancelled.
+      searchOperation.getClientConnection().deregisterPersistentSearch(this);
+
+      // Notify any cancellation callbacks.
+      for (CancellationCallback callback : cancellationCallbacks)
+      {
+        try
+        {
+          callback.persistentSearchCancelled(this);
+        }
+        catch (Exception e)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
+        }
+      }
+    }
+
+    return new CancelResult(ResultCode.CANCELED, null);
   }
 
 
 
   /**
-   * Retrieves the set of change types for this persistent search.
+   * Gets the message ID associated with this persistent search.
    *
-   * @return  The set of change types for this persistent search.
+   * @return The message ID associated with this persistent search.
    */
-  public Set<PersistentSearchChangeType> getChangeTypes()
+  public int getMessageID()
   {
-    return changeTypes;
+    return searchOperation.getMessageID();
   }
 
 
 
   /**
-   * Retrieves the returnECs flag for this persistent search.
+   * Notifies the persistent searches that an entry has been added.
    *
-   * @return  The return ECs flag for this persistent search.
+   * @param entry
+   *          The entry that was added.
+   * @param changeNumber
+   *          The change number associated with the operation that
+   *          added the entry, or {@code -1} if there is no change
+   *          number.
    */
-  public boolean getReturnECs()
-  {
-    return returnECs;
-  }
-
-
-
-  /**
-   * Retrieves the base DN for this persistent search.
-   *
-   * @return  The base DN for this persistent search.
-   */
-  public DN getBaseDN()
-  {
-    return baseDN;
-  }
-
-
-
-  /**
-   * Retrieves the scope for this persistent search.
-   *
-   * @return  The scope for this persistent search.
-   */
-  public SearchScope getScope()
-  {
-    return scope;
-  }
-
-
-
-  /**
-   * Retrieves the filter for this persistent search.
-   *
-   * @return  The filter for this persistent search.
-   */
-  public SearchFilter getFilter()
-  {
-    return filter;
-  }
-
-
-
-  /**
-   * Performs any necessary processing for the provided add operation.
-   *
-   * @param  addOperation  The add operation that has been processed.
-   * @param  entry         The entry that was added.
-   */
-  public void processAdd(LocalBackendAddOperation addOperation, Entry entry)
+  public void processAdd(Entry entry, long changeNumber)
   {
     // See if we care about add operations.
-    if (! changeTypes.contains(PersistentSearchChangeType.ADD))
+    if (!changeTypes.contains(PersistentSearchChangeType.ADD))
     {
       return;
     }
 
-
     // Make sure that the entry is within our target scope.
     switch (scope)
     {
-      case BASE_OBJECT:
-        if (! baseDN.equals(entry.getDN()))
-        {
-          return;
-        }
-        break;
-      case SINGLE_LEVEL:
-        if (! baseDN.equals(entry.getDN().getParentDNInSuffix()))
-        {
-          return;
-        }
-        break;
-      case WHOLE_SUBTREE:
-        if (! baseDN.isAncestorOf(entry.getDN()))
-        {
-          return;
-        }
-        break;
-      case SUBORDINATE_SUBTREE:
-        if (baseDN.equals(entry.getDN()) ||
-            (! baseDN.isAncestorOf(entry.getDN())))
-        {
-          return;
-        }
-        break;
-      default:
+    case BASE_OBJECT:
+      if (!baseDN.equals(entry.getDN()))
+      {
         return;
+      }
+      break;
+    case SINGLE_LEVEL:
+      if (!baseDN.equals(entry.getDN().getParentDNInSuffix()))
+      {
+        return;
+      }
+      break;
+    case WHOLE_SUBTREE:
+      if (!baseDN.isAncestorOf(entry.getDN()))
+      {
+        return;
+      }
+      break;
+    case SUBORDINATE_SUBTREE:
+      if (baseDN.equals(entry.getDN()) || (!baseDN.isAncestorOf(entry.getDN())))
+      {
+        return;
+      }
+      break;
+    default:
+      return;
     }
 
-
     // Make sure that the entry matches the target filter.
     try
     {
-      if (! filter.matchesEntry(entry))
+      if (!filter.matchesEntry(entry))
       {
         return;
       }
@@ -243,25 +256,22 @@
       return;
     }
 
-
-    // The entry is one that should be sent to the client.  See if we also need
-    // to construct an entry change notification control.
+    // The entry is one that should be sent to the client. See if we
+    // also need to construct an entry change notification control.
     ArrayList<Control> entryControls = new ArrayList<Control>(1);
     if (returnECs)
     {
       entryControls.add(new EntryChangeNotificationControl(
-                                 PersistentSearchChangeType.ADD,
-                                 addOperation.getChangeNumber()));
+          PersistentSearchChangeType.ADD, changeNumber));
     }
 
-
-    // Send the entry and see if we should continue processing.  If not, then
-    // deregister this persistent search.
+    // Send the entry and see if we should continue processing. If
+    // not, then deregister this persistent search.
     try
     {
-      if (! searchOperation.returnEntry(entry, entryControls))
+      if (!searchOperation.returnEntry(entry, entryControls))
       {
-        DirectoryServer.deregisterPersistentSearch(this);
+        cancel();
         searchOperation.sendSearchResultDone();
       }
     }
@@ -272,7 +282,7 @@
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
 
-      DirectoryServer.deregisterPersistentSearch(this);
+      cancel();
 
       try
       {
@@ -291,58 +301,58 @@
 
 
   /**
-   * Performs any necessary processing for the provided delete operation.
+   * Notifies the persistent searches that an entry has been deleted.
    *
-   * @param  deleteOperation  The delete operation that has been processed.
-   * @param  entry            The entry that was removed.
+   * @param entry
+   *          The entry that was deleted.
+   * @param changeNumber
+   *          The change number associated with the operation that
+   *          deleted the entry, or {@code -1} if there is no change
+   *          number.
    */
-  public void processDelete(LocalBackendDeleteOperation deleteOperation,
-      Entry entry)
+  public void processDelete(Entry entry, long changeNumber)
   {
     // See if we care about delete operations.
-    if (! changeTypes.contains(PersistentSearchChangeType.DELETE))
+    if (!changeTypes.contains(PersistentSearchChangeType.DELETE))
     {
       return;
     }
 
-
     // Make sure that the entry is within our target scope.
     switch (scope)
     {
-      case BASE_OBJECT:
-        if (! baseDN.equals(entry.getDN()))
-        {
-          return;
-        }
-        break;
-      case SINGLE_LEVEL:
-        if (! baseDN.equals(entry.getDN().getParentDNInSuffix()))
-        {
-          return;
-        }
-        break;
-      case WHOLE_SUBTREE:
-        if (! baseDN.isAncestorOf(entry.getDN()))
-        {
-          return;
-        }
-        break;
-      case SUBORDINATE_SUBTREE:
-        if (baseDN.equals(entry.getDN()) ||
-            (! baseDN.isAncestorOf(entry.getDN())))
-        {
-          return;
-        }
-        break;
-      default:
+    case BASE_OBJECT:
+      if (!baseDN.equals(entry.getDN()))
+      {
         return;
+      }
+      break;
+    case SINGLE_LEVEL:
+      if (!baseDN.equals(entry.getDN().getParentDNInSuffix()))
+      {
+        return;
+      }
+      break;
+    case WHOLE_SUBTREE:
+      if (!baseDN.isAncestorOf(entry.getDN()))
+      {
+        return;
+      }
+      break;
+    case SUBORDINATE_SUBTREE:
+      if (baseDN.equals(entry.getDN()) || (!baseDN.isAncestorOf(entry.getDN())))
+      {
+        return;
+      }
+      break;
+    default:
+      return;
     }
 
-
     // Make sure that the entry matches the target filter.
     try
     {
-      if (! filter.matchesEntry(entry))
+      if (!filter.matchesEntry(entry))
       {
         return;
       }
@@ -359,25 +369,22 @@
       return;
     }
 
-
-    // The entry is one that should be sent to the client.  See if we also need
-    // to construct an entry change notification control.
+    // The entry is one that should be sent to the client. See if we
+    // also need to construct an entry change notification control.
     ArrayList<Control> entryControls = new ArrayList<Control>(1);
     if (returnECs)
     {
       entryControls.add(new EntryChangeNotificationControl(
-                                 PersistentSearchChangeType.DELETE,
-                                 deleteOperation.getChangeNumber()));
+          PersistentSearchChangeType.DELETE, changeNumber));
     }
 
-
-    // Send the entry and see if we should continue processing.  If not, then
-    // deregister this persistent search.
+    // Send the entry and see if we should continue processing. If
+    // not, then deregister this persistent search.
     try
     {
-      if (! searchOperation.returnEntry(entry, entryControls))
+      if (!searchOperation.returnEntry(entry, entryControls))
       {
-        DirectoryServer.deregisterPersistentSearch(this);
+        cancel();
         searchOperation.sendSearchResultDone();
       }
     }
@@ -388,7 +395,7 @@
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
 
-      DirectoryServer.deregisterPersistentSearch(this);
+      cancel();
 
       try
       {
@@ -407,61 +414,78 @@
 
 
   /**
-   * Performs any necessary processing for the provided modify operation.
+   * Notifies the persistent searches that an entry has been modified.
    *
-   * @param  modifyOperation  The modify operation that has been processed.
-   * @param  oldEntry         The entry before the modification was applied.
-   * @param  newEntry         The entry after the modification was applied.
+   * @param entry
+   *          The entry after it was modified.
+   * @param changeNumber
+   *          The change number associated with the operation that
+   *          modified the entry, or {@code -1} if there is no change
+   *          number.
    */
-  public void processModify(LocalBackendModifyOperation modifyOperation,
-                            Entry oldEntry,
-                            Entry newEntry)
+  public void processModify(Entry entry, long changeNumber)
+  {
+    processModify(entry, changeNumber, entry);
+  }
+
+
+
+  /**
+   * Notifies persistent searches that an entry has been modified.
+   *
+   * @param entry
+   *          The entry after it was modified.
+   * @param changeNumber
+   *          The change number associated with the operation that
+   *          modified the entry, or {@code -1} if there is no change
+   *          number.
+   * @param oldEntry
+   *          The entry before it was modified.
+   */
+  public void processModify(Entry entry, long changeNumber, Entry oldEntry)
   {
     // See if we care about modify operations.
-    if (! changeTypes.contains(PersistentSearchChangeType.MODIFY))
+    if (!changeTypes.contains(PersistentSearchChangeType.MODIFY))
     {
       return;
     }
 
-
     // Make sure that the entry is within our target scope.
     switch (scope)
     {
-      case BASE_OBJECT:
-        if (! baseDN.equals(oldEntry.getDN()))
-        {
-          return;
-        }
-        break;
-      case SINGLE_LEVEL:
-        if (! baseDN.equals(oldEntry.getDN().getParentDNInSuffix()))
-        {
-          return;
-        }
-        break;
-      case WHOLE_SUBTREE:
-        if (! baseDN.isAncestorOf(oldEntry.getDN()))
-        {
-          return;
-        }
-        break;
-      case SUBORDINATE_SUBTREE:
-        if (baseDN.equals(oldEntry.getDN()) ||
-            (! baseDN.isAncestorOf(oldEntry.getDN())))
-        {
-          return;
-        }
-        break;
-      default:
+    case BASE_OBJECT:
+      if (!baseDN.equals(oldEntry.getDN()))
+      {
         return;
+      }
+      break;
+    case SINGLE_LEVEL:
+      if (!baseDN.equals(oldEntry.getDN().getParent()))
+      {
+        return;
+      }
+      break;
+    case WHOLE_SUBTREE:
+      if (!baseDN.isAncestorOf(oldEntry.getDN()))
+      {
+        return;
+      }
+      break;
+    case SUBORDINATE_SUBTREE:
+      if (baseDN.equals(oldEntry.getDN())
+          || (!baseDN.isAncestorOf(oldEntry.getDN())))
+      {
+        return;
+      }
+      break;
+    default:
+      return;
     }
 
-
     // Make sure that the entry matches the target filter.
     try
     {
-      if ((! filter.matchesEntry(oldEntry)) &&
-          (! filter.matchesEntry(newEntry)))
+      if ((!filter.matchesEntry(oldEntry)) && (!filter.matchesEntry(entry)))
       {
         return;
       }
@@ -478,25 +502,22 @@
       return;
     }
 
-
-    // The entry is one that should be sent to the client.  See if we also need
-    // to construct an entry change notification control.
+    // The entry is one that should be sent to the client. See if we
+    // also need to construct an entry change notification control.
     ArrayList<Control> entryControls = new ArrayList<Control>(1);
     if (returnECs)
     {
       entryControls.add(new EntryChangeNotificationControl(
-                                 PersistentSearchChangeType.MODIFY,
-                                 modifyOperation.getChangeNumber()));
+          PersistentSearchChangeType.MODIFY, changeNumber));
     }
 
-
-    // Send the entry and see if we should continue processing.  If not, then
-    // deregister this persistent search.
+    // Send the entry and see if we should continue processing. If
+    // not, then deregister this persistent search.
     try
     {
-      if (! searchOperation.returnEntry(newEntry, entryControls))
+      if (!searchOperation.returnEntry(entry, entryControls))
       {
-        DirectoryServer.deregisterPersistentSearch(this);
+        cancel();
         searchOperation.sendSearchResultDone();
       }
     }
@@ -507,7 +528,7 @@
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
 
-      DirectoryServer.deregisterPersistentSearch(this);
+      cancel();
 
       try
       {
@@ -526,82 +547,83 @@
 
 
   /**
-   * Performs any necessary processing for the provided modify DN operation.
+   * Notifies the persistent searches that an entry has been renamed.
    *
-   * @param  modifyDNOperation  The modify DN operation that has been processed.
-   * @param  oldEntry           The entry before the modify DN.
-   * @param  newEntry           The entry after the modify DN.
+   * @param entry
+   *          The entry after it was modified.
+   * @param changeNumber
+   *          The change number associated with the operation that
+   *          modified the entry, or {@code -1} if there is no change
+   *          number.
+   * @param oldDN
+   *          The DN of the entry before it was renamed.
    */
-  public void processModifyDN(LocalBackendModifyDNOperation modifyDNOperation,
-                              Entry oldEntry, Entry newEntry)
+  public void processModifyDN(Entry entry, long changeNumber, DN oldDN)
   {
     // See if we care about modify DN operations.
-    if (! changeTypes.contains(PersistentSearchChangeType.MODIFY_DN))
+    if (!changeTypes.contains(PersistentSearchChangeType.MODIFY_DN))
     {
       return;
     }
 
-
-    // Make sure that the old or new entry is within our target scope.  In this
-    // case, we need to check the DNs of both the old and new entry so we know
-    // which one(s) should be compared against the filter.
+    // Make sure that the old or new entry is within our target scope.
+    // In this case, we need to check the DNs of both the old and new
+    // entry so we know which one(s) should be compared against the
+    // filter.
     boolean oldMatches = false;
     boolean newMatches = false;
 
     switch (scope)
     {
-      case BASE_OBJECT:
-        oldMatches = baseDN.equals(oldEntry.getDN());
-        newMatches = baseDN.equals(newEntry.getDN());
+    case BASE_OBJECT:
+      oldMatches = baseDN.equals(oldDN);
+      newMatches = baseDN.equals(entry.getDN());
 
-        if (! (oldMatches || newMatches))
-        {
-          return;
-        }
-
-        break;
-      case SINGLE_LEVEL:
-        oldMatches = baseDN.equals(oldEntry.getDN().getParentDNInSuffix());
-        newMatches = baseDN.equals(newEntry.getDN().getParentDNInSuffix());
-
-        if (! (oldMatches || newMatches))
-        {
-          return;
-        }
-
-        break;
-      case WHOLE_SUBTREE:
-        oldMatches = baseDN.isAncestorOf(oldEntry.getDN());
-        newMatches = baseDN.isAncestorOf(newEntry.getDN());
-
-        if (! (oldMatches || newMatches))
-        {
-          return;
-        }
-
-        break;
-      case SUBORDINATE_SUBTREE:
-        oldMatches = ((! baseDN.equals(oldEntry.getDN())) &&
-                      baseDN.isAncestorOf(oldEntry.getDN()));
-        newMatches = ((! baseDN.equals(newEntry.getDN())) &&
-                      baseDN.isAncestorOf(newEntry.getDN()));
-
-        if (! (oldMatches || newMatches))
-        {
-          return;
-        }
-
-        break;
-      default:
+      if (!(oldMatches || newMatches))
+      {
         return;
-    }
+      }
 
+      break;
+    case SINGLE_LEVEL:
+      oldMatches = baseDN.equals(oldDN.getParent());
+      newMatches = baseDN.equals(entry.getDN().getParent());
+
+      if (!(oldMatches || newMatches))
+      {
+        return;
+      }
+
+      break;
+    case WHOLE_SUBTREE:
+      oldMatches = baseDN.isAncestorOf(oldDN);
+      newMatches = baseDN.isAncestorOf(entry.getDN());
+
+      if (!(oldMatches || newMatches))
+      {
+        return;
+      }
+
+      break;
+    case SUBORDINATE_SUBTREE:
+      oldMatches = ((!baseDN.equals(oldDN)) && baseDN.isAncestorOf(oldDN));
+      newMatches = ((!baseDN.equals(entry.getDN())) && baseDN
+          .isAncestorOf(entry.getDN()));
+
+      if (!(oldMatches || newMatches))
+      {
+        return;
+      }
+
+      break;
+    default:
+      return;
+    }
 
     // Make sure that the entry matches the target filter.
     try
     {
-      if (((! oldMatches) || (! filter.matchesEntry(oldEntry))) &&
-          ((! newMatches) && (! filter.matchesEntry(newEntry))))
+      if (!oldMatches && !newMatches && !filter.matchesEntry(entry))
       {
         return;
       }
@@ -618,26 +640,22 @@
       return;
     }
 
-
-    // The entry is one that should be sent to the client.  See if we also need
-    // to construct an entry change notification control.
+    // The entry is one that should be sent to the client. See if we
+    // also need to construct an entry change notification control.
     ArrayList<Control> entryControls = new ArrayList<Control>(1);
     if (returnECs)
     {
       entryControls.add(new EntryChangeNotificationControl(
-                                 PersistentSearchChangeType.MODIFY_DN,
-                                 oldEntry.getDN(),
-                                 modifyDNOperation.getChangeNumber()));
+          PersistentSearchChangeType.MODIFY_DN, oldDN, changeNumber));
     }
 
-
-    // Send the entry and see if we should continue processing.  If not, then
-    // deregister this persistent search.
+    // Send the entry and see if we should continue processing. If
+    // not, then deregister this persistent search.
     try
     {
-      if (! searchOperation.returnEntry(newEntry, entryControls))
+      if (!searchOperation.returnEntry(entry, entryControls))
       {
-        DirectoryServer.deregisterPersistentSearch(this);
+        cancel();
         searchOperation.sendSearchResultDone();
       }
     }
@@ -648,7 +666,7 @@
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
 
-      DirectoryServer.deregisterPersistentSearch(this);
+      cancel();
 
       try
       {
@@ -667,10 +685,26 @@
 
 
   /**
+   * Registers a cancellation callback with this persistent search.
+   * The cancellation callback will be notified when this persistent
+   * search has been cancelled.
+   *
+   * @param callback
+   *          The cancellation callback.
+   */
+  public void registerCancellationCallback(CancellationCallback callback)
+  {
+    cancellationCallbacks.add(callback);
+  }
+
+
+
+  /**
    * Retrieves a string representation of this persistent search.
    *
-   * @return  A string representation of this persistent search.
+   * @return A string representation of this persistent search.
    */
+  @Override
   public String toString()
   {
     StringBuilder buffer = new StringBuilder();
@@ -681,10 +715,11 @@
 
 
   /**
-   * Appends a string representation of this persistent search to the provided
-   * buffer.
+   * Appends a string representation of this persistent search to the
+   * provided buffer.
    *
-   * @param  buffer  The buffer to which the information should be appended.
+   * @param buffer
+   *          The buffer to which the information should be appended.
    */
   public void toString(StringBuilder buffer)
   {
@@ -701,4 +736,3 @@
     buffer.append("\")");
   }
 }
-
diff --git a/opends/src/server/org/opends/server/core/SearchOperationBasis.java b/opends/src/server/org/opends/server/core/SearchOperationBasis.java
index f296b93..4c2b312 100644
--- a/opends/src/server/org/opends/server/core/SearchOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -1277,7 +1277,7 @@
 
       if (persistentSearch != null)
       {
-        DirectoryServer.deregisterPersistentSearch(persistentSearch);
+        persistentSearch.cancel();
         persistentSearch = null;
       }
     }
diff --git a/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java b/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
index 34d09f0..69c1571 100644
--- a/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -141,7 +141,7 @@
   private AuthenticationInfo authenticationInfo;
 
   // The empty operation list for this connection.
-  private LinkedList<AbstractOperation> operationList;
+  private LinkedList<Operation> operationList;
 
   // The connection ID for this client connection.
   private long connectionID;
@@ -258,7 +258,7 @@
     }
 
     connectionID  = nextConnectionID.getAndDecrement();
-    operationList = new LinkedList<AbstractOperation>();
+    operationList = new LinkedList<Operation>();
 
     try
     {
@@ -298,7 +298,7 @@
     super.setLookthroughLimit(0);
 
     connectionID  = nextConnectionID.getAndDecrement();
-    operationList = new LinkedList<AbstractOperation>();
+    operationList = new LinkedList<Operation>();
 
     try
     {
@@ -2919,7 +2919,7 @@
        mayExtend=false,
        mayInvoke=false)
   @Override()
-  public Collection<AbstractOperation> getOperationsInProgress()
+  public Collection<Operation> getOperationsInProgress()
   {
     return operationList;
   }
diff --git a/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java b/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
index 5c3387b..62f5abd 100644
--- a/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
@@ -100,7 +100,7 @@
   private ConnectionSecurityProvider securityProvider;
 
   // The empty operation list for this connection.
-  private LinkedList<AbstractOperation> operationList;
+  private LinkedList<Operation> operationList;
 
   // The connection ID for this client connection.
   private long connectionID;
@@ -155,7 +155,7 @@
           true,
           ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get());
     }
-    operationList = new LinkedList<AbstractOperation>();
+    operationList = new LinkedList<Operation>();
 
     try
     {
@@ -1130,7 +1130,7 @@
    *
    * @return  The set of operations in progress for this client connection.
    */
-  public Collection<AbstractOperation> getOperationsInProgress()
+  public Collection<Operation> getOperationsInProgress()
   {
     return operationList;
   }
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 1586159..d7eee43 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -138,7 +138,7 @@
   private byte[] elementValue;
 
   // The set of all operations currently in progress on this connection.
-  private ConcurrentHashMap<Integer,AbstractOperation> operationsInProgress;
+  private ConcurrentHashMap<Integer,Operation> operationsInProgress;
 
   // The number of operations performed on this connection.
   // Used to compare with the resource limits of the network group.
@@ -264,7 +264,7 @@
     nextOperationID      = new AtomicLong(0);
     connectionValid      = true;
     disconnectRequested  = false;
-    operationsInProgress = new ConcurrentHashMap<Integer,AbstractOperation>();
+    operationsInProgress = new ConcurrentHashMap<Integer,Operation>();
     operationsPerformed = 0;
     operationsPerformedLock = new Object();
     keepStats            = connectionHandler.keepStats();
@@ -1136,7 +1136,7 @@
    *
    * @return  The set of operations in progress for this client connection.
    */
-  public Collection<AbstractOperation> getOperationsInProgress()
+  public Collection<Operation> getOperationsInProgress()
   {
     return operationsInProgress.values();
   }
@@ -1151,7 +1151,7 @@
    * @return  The operation in progress with the specified message ID, or
    *          <CODE>null</CODE> if no such operation could be found.
    */
-  public AbstractOperation getOperationInProgress(int messageID)
+  public Operation getOperationInProgress(int messageID)
   {
     return operationsInProgress.get(messageID);
   }
@@ -1192,7 +1192,7 @@
 
         // See if there is already an operation in progress with the same
         // message ID.  If so, then we can't allow it.
-        AbstractOperation op = operationsInProgress.get(messageID);
+        Operation op = operationsInProgress.get(messageID);
         if (op != null)
         {
           Message message =
@@ -1252,7 +1252,7 @@
    */
   public boolean removeOperationInProgress(int messageID)
   {
-    AbstractOperation operation = operationsInProgress.remove(messageID);
+    Operation operation = operationsInProgress.remove(messageID);
     if (operation == null)
     {
       return false;
@@ -1279,16 +1279,15 @@
   public CancelResult cancelOperation(int messageID,
                                       CancelRequest cancelRequest)
   {
-    AbstractOperation op = operationsInProgress.get(messageID);
+    Operation op = operationsInProgress.get(messageID);
     if (op == null)
     {
       // See if the operation is in the list of persistent searches.
       for (PersistentSearch ps : getPersistentSearches())
       {
-        if (ps.getSearchOperation().getMessageID() == messageID)
+        if (ps.getMessageID() == messageID)
         {
-          CancelResult cancelResult =
-               ps.getSearchOperation().cancel(cancelRequest);
+          CancelResult cancelResult = ps.cancel();
 
           if (keepStats && (cancelResult.getResultCode() ==
               ResultCode.CANCELED))
@@ -1329,11 +1328,11 @@
     {
       try
       {
-        for (AbstractOperation o : operationsInProgress.values())
+        for (Operation o : operationsInProgress.values())
         {
           try
           {
-              o.abort(cancelRequest);
+            o.abort(cancelRequest);
 
             // TODO: Assume its cancelled?
             if (keepStats)
@@ -1361,7 +1360,7 @@
 
         for (PersistentSearch persistentSearch : getPersistentSearches())
         {
-          DirectoryServer.deregisterPersistentSearch(persistentSearch);
+          persistentSearch.cancel();
         }
       }
       catch (Exception e)
@@ -1400,7 +1399,7 @@
             continue;
           }
 
-          AbstractOperation o = operationsInProgress.get(msgID);
+          Operation o = operationsInProgress.get(msgID);
           if (o != null)
           {
             try
@@ -1429,7 +1428,7 @@
 
         for (PersistentSearch persistentSearch : getPersistentSearches())
         {
-          DirectoryServer.deregisterPersistentSearch(persistentSearch);
+          persistentSearch.cancel();
           lastCompletionTime.set(TimeThread.getTime());
         }
       }
diff --git a/opends/src/server/org/opends/server/types/AbstractOperation.java b/opends/src/server/org/opends/server/types/AbstractOperation.java
index e9a365b..d7dae8c 100644
--- a/opends/src/server/org/opends/server/types/AbstractOperation.java
+++ b/opends/src/server/org/opends/server/types/AbstractOperation.java
@@ -33,12 +33,14 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.types.operation.PostResponseOperation;
 import org.opends.server.types.operation.PreParseOperation;
 import org.opends.server.core.DirectoryServer;
+
 import static org.opends.server.loggers.debug.
     DebugLogger.debugEnabled;
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -168,6 +170,9 @@
   // nanoseconds.
   private long processingStopNanoTime;
 
+  // The callbacks to be invoked once a response has been sent.
+  private List<Runnable> postResponseCallbacks = null;
+
   /**
    * Creates a new operation with the provided information.
    *
@@ -1148,5 +1153,47 @@
    * processing.
    */
   public abstract void run();
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void registerPostResponseCallback(Runnable callback)
+  {
+    if (postResponseCallbacks == null)
+    {
+      postResponseCallbacks = new LinkedList<Runnable>();
+    }
+    postResponseCallbacks.add(callback);
+  }
+
+
+
+  /**
+   * Invokes the post response callbacks that were registered with
+   * this operation.
+   */
+  protected final void invokePostResponseCallbacks()
+  {
+    if (postResponseCallbacks != null)
+    {
+      for (Runnable callback : postResponseCallbacks)
+      {
+        try
+        {
+          callback.run();
+        }
+        catch (Exception e)
+        {
+          // Should not happen.
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
+        }
+      }
+    }
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/types/Operation.java b/opends/src/server/org/opends/server/types/Operation.java
index 2e46443..1d9bf0f 100644
--- a/opends/src/server/org/opends/server/types/Operation.java
+++ b/opends/src/server/org/opends/server/types/Operation.java
@@ -622,5 +622,15 @@
   public void checkIfCanceled(boolean signalTooLate)
       throws CanceledOperationException;
 
+  /**
+   * Registers a callback which should be run once this operation has
+   * completed and the response sent back to the client.
+   *
+   * @param callback
+   *          The callback to be run once this operation has completed
+   *          and the response sent back to the client.
+   */
+  public void registerPostResponseCallback(Runnable callback);
+
 }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index 985e55c..f1fdefe 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -65,6 +65,7 @@
 import org.opends.server.core.AddOperationWrapper;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.PasswordPolicy;
+import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.PluginConfigManager;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.schema.AuthPasswordSyntax;
@@ -173,16 +174,17 @@
   /**
    * Process this add operation against a local backend.
    *
-   * @param  backend  The backend in which the add operation should be
-   *                  processed.
-   *
-   * @throws CanceledOperationException if this operation should be
-   * cancelled
+   * @param wfe
+   *          The local backend work-flow element.
+   * @throws CanceledOperationException
+   *           if this operation should be cancelled
    */
-  void processLocalAdd(Backend backend) throws CanceledOperationException {
+  void processLocalAdd(final LocalBackendWorkflowElement wfe)
+      throws CanceledOperationException
+  {
     boolean executePostOpPlugins = false;
 
-    this.backend = backend;
+    this.backend = wfe.getBackend();
     ClientConnection clientConnection = getClientConnection();
 
     // Get the plugin config manager that will be used for invoking plugins.
@@ -772,29 +774,41 @@
       }
     }
 
-
-    // Notify any change notification listeners that might be registered with
-    // the server.
-    if ((getResultCode() == ResultCode.SUCCESS) && (entry != null))
+    // Register a post-response call-back which will notify persistent
+    // searches and change listeners.
+    if (getResultCode() == ResultCode.SUCCESS)
     {
-      for (ChangeNotificationListener changeListener :
-           DirectoryServer.getChangeNotificationListeners())
+      registerPostResponseCallback(new Runnable()
       {
-        try
+        public void run()
         {
-          changeListener.handleAddOperation(this, entry);
-        }
-        catch (Exception e)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          // Notify persistent searches.
+          for (PersistentSearch psearch : wfe.getPersistentSearches()) {
+            psearch.processAdd(entry, getChangeNumber());
           }
 
-          logError(ERR_ADD_ERROR_NOTIFYING_CHANGE_LISTENER.get(
-                        getExceptionMessage(e)));
+          // Notify change listeners.
+          for (ChangeNotificationListener changeListener : DirectoryServer
+              .getChangeNotificationListeners())
+          {
+            try
+            {
+              changeListener.handleAddOperation(LocalBackendAddOperation.this,
+                  entry);
+            }
+            catch (Exception e)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+
+              logError(ERR_ADD_ERROR_NOTIFYING_CHANGE_LISTENER
+                  .get(getExceptionMessage(e)));
+            }
+          }
         }
-      }
+      });
     }
   }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
index 191b614..bccc5df 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
@@ -178,12 +178,13 @@
   /**
    * Process this bind operation in a local backend.
    *
-   * @param  backend  The backend in which the bind operation should be
-   *                  processed.
+   * @param wfe
+   *          The local backend work-flow element.
+   *
    */
-  void processLocalBind(Backend backend)
+  void processLocalBind(LocalBackendWorkflowElement wfe)
   {
-    this.backend = backend;
+    this.backend = wfe.getBackend();
 
     // Initialize a number of variables for use during the bind processing.
     clientConnection         = getClientConnection();
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
index 950e7f6..83d2279 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -116,16 +116,17 @@
   /**
    * Process this compare operation in a local backend.
    *
-   * @param  backend  The backend in which the compare operation should be
-   *                  processed.
-   *
-   * @throws CanceledOperationException if this operation should be
-   * cancelled
+   * @param wfe
+   *          The local backend work-flow element.
+   * @throws CanceledOperationException
+   *           if this operation should be cancelled
    */
-  void processLocalCompare(Backend backend) throws CanceledOperationException {
+  void processLocalCompare(LocalBackendWorkflowElement wfe)
+      throws CanceledOperationException
+  {
     boolean executePostOpPlugins = false;
 
-    this.backend = backend;
+    this.backend = wfe.getBackend();
 
     clientConnection  = getClientConnection();
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
index 3e8571a..ccd4c22 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -47,6 +47,7 @@
 import org.opends.server.core.DeleteOperationWrapper;
 import org.opends.server.core.DeleteOperation;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.PluginConfigManager;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.AttributeType;
@@ -143,16 +144,16 @@
   /**
    * Process this delete operation in a local backend.
    *
-   * @param  backend  The backend in which the delete operation should be
-   *                  processed.
-   *
-   * @throws CanceledOperationException if this operation should be
-   * cancelled
+   * @param wfe
+   *          The local backend work-flow element.
+   * @throws CanceledOperationException
+   *           if this operation should be cancelled
    */
-  void processLocalDelete(Backend backend) throws CanceledOperationException {
+  void processLocalDelete(final LocalBackendWorkflowElement wfe)
+      throws CanceledOperationException
+  {
     boolean executePostOpPlugins = false;
-
-    this.backend = backend;
+    this.backend = wfe.getBackend();
 
     clientConnection = getClientConnection();
 
@@ -444,29 +445,43 @@
     }
 
 
-    // Notify any change notification listeners that might be registered with
-    // the server.
+    // Register a post-response call-back which will notify persistent
+    // searches and change listeners.
     if (getResultCode() == ResultCode.SUCCESS)
     {
-      for (ChangeNotificationListener changeListener :
-           DirectoryServer.getChangeNotificationListeners())
+      registerPostResponseCallback(new Runnable()
       {
-        try
+
+        public void run()
         {
-          changeListener.handleDeleteOperation(this, entry);
-        }
-        catch (Exception e)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          // Notify persistent searches.
+          for (PersistentSearch psearch : wfe.getPersistentSearches()) {
+            psearch.processDelete(entry, getChangeNumber());
           }
 
-          Message message = ERR_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER.get(
-              getExceptionMessage(e));
-          logError(message);
+          // Notify change listeners.
+          for (ChangeNotificationListener changeListener : DirectoryServer
+              .getChangeNotificationListeners())
+          {
+            try
+            {
+              changeListener.handleDeleteOperation(
+                  LocalBackendDeleteOperation.this, entry);
+            }
+            catch (Exception e)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+
+              Message message = ERR_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER
+                  .get(getExceptionMessage(e));
+              logError(message);
+            }
+          }
         }
-      }
+      });
     }
   }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
index d23006f..fcbedd5 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -51,6 +51,7 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyDNOperationWrapper;
+import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.PluginConfigManager;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.Attribute;
@@ -180,16 +181,16 @@
   /**
    * Process this modify DN operation in a local backend.
    *
-   * @param  backend  The backend in which the modify DN operation should be
-   *                  processed.
-   *
-   * @throws CanceledOperationException if this operation should be
-   * cancelled
+   * @param wfe
+   *          The local backend work-flow element.
+   * @throws CanceledOperationException
+   *           if this operation should be cancelled
    */
-  void processLocalModifyDN(Backend backend) throws CanceledOperationException {
+  void processLocalModifyDN(final LocalBackendWorkflowElement wfe)
+      throws CanceledOperationException
+  {
     boolean executePostOpPlugins = false;
-
-    this.backend = backend;
+    this.backend = wfe.getBackend();
 
     clientConnection = getClientConnection();
 
@@ -632,30 +633,45 @@
       }
     }
 
-
-    // Notify any change notification listeners that might be registered with
-    // the server.
+    // Register a post-response call-back which will notify persistent
+    // searches and change listeners.
     if (getResultCode() == ResultCode.SUCCESS)
     {
-      for (ChangeNotificationListener changeListener :
-           DirectoryServer.getChangeNotificationListeners())
+      registerPostResponseCallback(new Runnable()
       {
-        try
+
+        public void run()
         {
-          changeListener.handleModifyDNOperation(this, currentEntry, newEntry);
-        }
-        catch (Exception e)
-        {
-          if (debugEnabled())
+          // Notify persistent searches.
+          for (PersistentSearch psearch : wfe.getPersistentSearches())
           {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            psearch.processModifyDN(newEntry, getChangeNumber(),
+                currentEntry.getDN());
           }
 
-          Message message = ERR_MODDN_ERROR_NOTIFYING_CHANGE_LISTENER.get(
-              getExceptionMessage(e));
-          logError(message);
+          // Notify change listeners.
+          for (ChangeNotificationListener changeListener : DirectoryServer
+              .getChangeNotificationListeners())
+          {
+            try
+            {
+              changeListener.handleModifyDNOperation(
+                  LocalBackendModifyDNOperation.this, currentEntry, newEntry);
+            }
+            catch (Exception e)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+
+              Message message = ERR_MODDN_ERROR_NOTIFYING_CHANGE_LISTENER
+                  .get(getExceptionMessage(e));
+              logError(message);
+            }
+          }
         }
-      }
+      });
     }
   }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
index 0f903f0..dc83bc6 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -65,6 +65,7 @@
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.ModifyOperationWrapper;
 import org.opends.server.core.PasswordPolicyState;
+import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.PluginConfigManager;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.protocols.asn1.ASN1OctetString;
@@ -293,16 +294,16 @@
   /**
    * Process this modify operation against a local backend.
    *
-   * @param  backend  The backend in which the modify operation should be
-   *                  performed.
-   *
-   * @throws CanceledOperationException if this operation should be
-   * cancelled
+   * @param wfe
+   *          The local backend work-flow element.
+   * @throws CanceledOperationException
+   *           if this operation should be cancelled
    */
-  void processLocalModify(Backend backend) throws CanceledOperationException {
+  void processLocalModify(final LocalBackendWorkflowElement wfe)
+      throws CanceledOperationException
+  {
     boolean executePostOpPlugins = false;
-
-    this.backend = backend;
+    this.backend = wfe.getBackend();
 
     clientConnection = getClientConnection();
 
@@ -690,11 +691,46 @@
     }
 
 
-    // Notify any change notification listeners that might be registered with
-    // the server.
+    // Register a post-response call-back which will notify persistent
+    // searches and change listeners.
     if (getResultCode() == ResultCode.SUCCESS)
     {
-      notifyChangeListeners();
+      registerPostResponseCallback(new Runnable()
+      {
+
+        public void run()
+        {
+          // Notify persistent searches.
+          for (PersistentSearch psearch : wfe.getPersistentSearches())
+          {
+            psearch.processModify(modifiedEntry, getChangeNumber(),
+                currentEntry);
+          }
+
+          // Notify change listeners.
+          for (ChangeNotificationListener changeListener : DirectoryServer
+              .getChangeNotificationListeners())
+          {
+            try
+            {
+              changeListener
+                  .handleModifyOperation(LocalBackendModifyOperation.this,
+                      currentEntry, modifiedEntry);
+            }
+            catch (Exception e)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+
+              Message message = ERR_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER
+                  .get(getExceptionMessage(e));
+              logError(message);
+            }
+          }
+        }
+      });
     }
   }
 
@@ -2228,32 +2264,6 @@
 
 
 
-  /**
-   * Notify any registered change listeners about this update.
-   */
-  private void notifyChangeListeners()
-  {
-    for (ChangeNotificationListener changeListener :
-         DirectoryServer.getChangeNotificationListeners())
-    {
-      try
-      {
-        changeListener.handleModifyOperation(this, currentEntry, modifiedEntry);
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        Message message = ERR_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER.get(
-            getExceptionMessage(e));
-        logError(message);
-      }
-    }
-  }
-
   private boolean handleConflictResolution() {
       boolean returnVal = true;
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
index 9f966e3..293cb99 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -121,16 +121,16 @@
   /**
    * Process this search operation against a local backend.
    *
-   * @param  backend  The backend in which the search operation should be
-   *                  performed.
-   *
-   * @throws CanceledOperationException if this operation should be
-   * cancelled
+   * @param wfe
+   *          The local backend work-flow element.
+   * @throws CanceledOperationException
+   *           if this operation should be cancelled
    */
-  void processLocalSearch(Backend backend) throws CanceledOperationException {
+  void processLocalSearch(LocalBackendWorkflowElement wfe)
+      throws CanceledOperationException
+  {
     boolean executePostOpPlugins = false;
-
-    this.backend = backend;
+    this.backend = wfe.getBackend();
 
     clientConnection = getClientConnection();
 
@@ -230,7 +230,8 @@
       // If there's a persistent search, then register it with the server.
       if (persistentSearch != null)
       {
-        DirectoryServer.registerPersistentSearch(persistentSearch);
+        wfe.registerPersistentSearch(persistentSearch);
+        clientConnection.registerPersistentSearch(persistentSearch);
         setSendResponse(false);
       }
 
@@ -254,7 +255,7 @@
 
         if (persistentSearch != null)
         {
-          DirectoryServer.deregisterPersistentSearch(persistentSearch);
+          persistentSearch.cancel();
           setSendResponse(true);
         }
 
@@ -264,7 +265,7 @@
       {
         if (persistentSearch != null)
         {
-          DirectoryServer.deregisterPersistentSearch(persistentSearch);
+          persistentSearch.cancel();
           setSendResponse(true);
         }
 
@@ -283,7 +284,7 @@
 
         if (persistentSearch != null)
         {
-          DirectoryServer.deregisterPersistentSearch(persistentSearch);
+          persistentSearch.cancel();
           setSendResponse(true);
         }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 70b58ba..839ae6e 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -31,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.TreeMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -47,6 +48,7 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.SearchOperation;
 import org.opends.server.types.*;
 import org.opends.server.workflowelement.LeafWorkflowElement;
@@ -73,6 +75,10 @@
        registeredLocalBackends =
             new TreeMap<String, LocalBackendWorkflowElement>();
 
+  // The set of persistent searches registered with this work flow
+  // element.
+  private final List<PersistentSearch> persistentSearches =
+    new CopyOnWriteArrayList<PersistentSearch>();
 
   // a lock to guarantee safe concurrent access to the registeredLocalBackends
   // variable
@@ -154,6 +160,12 @@
     // an NPE
     super.initialize(null, null);
     backend = null;
+
+    // Cancel all persistent searches.
+    for (PersistentSearch psearch : persistentSearches) {
+      psearch.cancel();
+    }
+    persistentSearches.clear();
   }
 
 
@@ -372,43 +384,43 @@
       case BIND:
         LocalBackendBindOperation bindOperation =
              new LocalBackendBindOperation((BindOperation) operation);
-        bindOperation.processLocalBind(backend);
+        bindOperation.processLocalBind(this);
         break;
 
       case SEARCH:
         LocalBackendSearchOperation searchOperation =
              new LocalBackendSearchOperation((SearchOperation) operation);
-        searchOperation.processLocalSearch(backend);
+        searchOperation.processLocalSearch(this);
         break;
 
       case ADD:
         LocalBackendAddOperation addOperation =
              new LocalBackendAddOperation((AddOperation) operation);
-        addOperation.processLocalAdd(backend);
+        addOperation.processLocalAdd(this);
         break;
 
       case DELETE:
         LocalBackendDeleteOperation deleteOperation =
              new LocalBackendDeleteOperation((DeleteOperation) operation);
-        deleteOperation.processLocalDelete(backend);
+        deleteOperation.processLocalDelete(this);
         break;
 
       case MODIFY:
         LocalBackendModifyOperation modifyOperation =
              new LocalBackendModifyOperation((ModifyOperation) operation);
-        modifyOperation.processLocalModify(backend);
+        modifyOperation.processLocalModify(this);
         break;
 
       case MODIFY_DN:
         LocalBackendModifyDNOperation modifyDNOperation =
              new LocalBackendModifyDNOperation((ModifyDNOperation) operation);
-        modifyDNOperation.processLocalModifyDN(backend);
+        modifyDNOperation.processLocalModifyDN(this);
         break;
 
       case COMPARE:
         LocalBackendCompareOperation compareOperation =
              new LocalBackendCompareOperation((CompareOperation) operation);
-        compareOperation.processLocalCompare(backend);
+        compareOperation.processLocalCompare(this);
         break;
 
       case ABANDON:
@@ -456,5 +468,58 @@
                                   newAttachment);
   }
 
+
+
+  /**
+   * Gets the backend associated with this local backend workflow
+   * element.
+   *
+   * @return The backend associated with this local backend workflow
+   *         element.
+   */
+  Backend getBackend()
+  {
+    return backend;
+  }
+
+
+
+  /**
+   * Registers the provided persistent search operation with this
+   * local backend workflow element so that it will be notified of any
+   * add, delete, modify, or modify DN operations that are performed.
+   *
+   * @param persistentSearch
+   *          The persistent search operation to register with this
+   *          local backend workflow element.
+   */
+  void registerPersistentSearch(PersistentSearch persistentSearch)
+  {
+    PersistentSearch.CancellationCallback callback =
+      new PersistentSearch.CancellationCallback()
+    {
+      public void persistentSearchCancelled(PersistentSearch psearch)
+      {
+        persistentSearches.remove(psearch);
+      }
+    };
+
+    persistentSearches.add(persistentSearch);
+    persistentSearch.registerCancellationCallback(callback);
+  }
+
+
+
+  /**
+   * Gets the list of persistent searches currently active against
+   * this local backend workflow element.
+   *
+   * @return The list of persistent searches currently active against
+   *         this local backend workflow element.
+   */
+  List<PersistentSearch> getPersistentSearches()
+  {
+    return persistentSearches;
+  }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
index 57d32b5..ac5386e 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
@@ -54,7 +54,6 @@
 import org.opends.server.protocols.ldap.LDAPAttribute;
 import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.protocols.ldap.LDAPModification;
-import org.opends.server.types.AbstractOperation;
 import org.opends.server.types.Attributes;
 import org.opends.server.types.AuthenticationInfo;
 import org.opends.server.types.CancelRequest;
@@ -65,6 +64,7 @@
 import org.opends.server.types.Entry;
 import org.opends.server.types.Modification;
 import org.opends.server.types.ModificationType;
+import org.opends.server.types.Operation;
 import org.opends.server.types.RawAttribute;
 import org.opends.server.types.RawModification;
 import org.opends.server.types.RDN;
@@ -1074,7 +1074,7 @@
   {
     InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
-    Collection<AbstractOperation> opList = conn.getOperationsInProgress();
+    Collection<Operation> opList = conn.getOperationsInProgress();
     assertNotNull(opList);
     assertTrue(opList.isEmpty());
   }

--
Gitblit v1.10.0