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/core/PersistentSearch.java | 630 ++++++++++++++++++++++++++++++---------------------------
1 files changed, 332 insertions(+), 298 deletions(-)
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("\")");
}
}
-
--
Gitblit v1.10.0