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

matthew_swift
13.22.2008 347a872b693e46c105e953ea331616c8a566d745
This change relates to issue 3567.

In certain scenarios (e.g. distribution) a single client side persistent search may result in multiple back-end persistent searches being launched. The current server design only supported a one to one relationship. This change removes the restriction:

* persistent search is no longer stored in the search operation

* client connection operation cancellation now supports having
multiple PersistentSearch objects per client search

* PersistentSearch operation cancellation now cancels all related
PersistentSearches

* PersistentSearch instances must be enabled before use: doing this
registers the persistent search with the client connection and
prevents the underlying search operation from having a result sent.

I have also updated the class' javadoc to include more complete usage information.
6 files modified
209 ■■■■ changed files
opends/src/server/org/opends/server/core/PersistentSearch.java 137 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperation.java 14 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperationBasis.java 27 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperationWrapper.java 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PersistentSearch.java
@@ -53,6 +53,33 @@
/**
 * This class defines a data structure that will be used to hold the
 * information necessary for processing a persistent search.
 * <p>
 * Work flow element implementations are responsible for managing the
 * persistent searches that they are currently handling.
 * <p>
 * Typically, a work flow element search operation will first decode
 * the persistent search control and construct a new {@code
 * PersistentSearch}.
 * <p>
 * Once the initial search result set has been returned and no errors
 * encountered, the work flow element implementation should register a
 * cancellation callback which will be invoked when the persistent
 * search is cancelled. This is achieved using
 * {@link #registerCancellationCallback(CancellationCallback)}. The
 * callback should make sure that any resources associated with the
 * {@code PersistentSearch} are released. This may included removing
 * the {@code PersistentSearch} from a list, or abandoning a
 * persistent search operation that has been sent to a remote server.
 * <p>
 * Finally, the {@code PersistentSearch} should be enabled using
 * {@link #enable()}. This method will register the {@code
 * PersistentSearch} with the client connection and notify the
 * underlying search operation that no result should be sent to the
 * client.
 * <p>
 * Work flow element implementations should {@link #cancel()} active
 * persistent searches when the work flow element fails or is shut
 * down.
 */
public final class PersistentSearch
{
@@ -81,34 +108,65 @@
   */
  private static final DebugTracer TRACER = getTracer();
  // Indicates whether entries returned should include the entry
  // change notification control.
  private final boolean returnECs;
  // Cancel a persistent search.
  private static synchronized void cancel(PersistentSearch psearch)
  {
    if (!psearch.isCancelled)
    {
      psearch.isCancelled = true;
      // The persistent search can no longer be cancelled.
      psearch.searchOperation.getClientConnection().deregisterPersistentSearch(
          psearch);
      // Notify any cancellation callbacks.
      for (CancellationCallback callback : psearch.cancellationCallbacks)
      {
        try
        {
          callback.persistentSearchCancelled(psearch);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
        }
      }
    }
  }
  // The base DN for the search operation.
  private final DN baseDN;
  // The set of change types we want to see.
  private final Set<PersistentSearchChangeType> changeTypes;
  // The scope for the search operation.
  private final SearchScope scope;
  // The filter for the search operation.
  private final SearchFilter filter;
  // The reference to the associated search operation.
  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>();
  // The set of change types we want to see.
  private final Set<PersistentSearchChangeType> changeTypes;
  // The filter for the search operation.
  private final SearchFilter filter;
  // Indicates whether or not this persistent search has already been
  // aborted.
  private boolean isCancelled = false;
  // Indicates whether entries returned should include the entry
  // change notification control.
  private final boolean returnECs;
  // The scope for the search operation.
  private final SearchScope scope;
  // The reference to the associated search operation.
  private final SearchOperation searchOperation;
  /**
@@ -140,7 +198,9 @@
  /**
   * 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.
   * it will have been released. In addition, any other persistent
   * searches that are associated with this persistent search will
   * also be canceled.
   *
   * @return The result of the cancellation.
   */
@@ -148,24 +208,18 @@
  {
    if (!isCancelled)
    {
      isCancelled = true;
      // Cancel this persistent search.
      cancel(this);
      // The persistent search can no longer be cancelled.
      searchOperation.getClientConnection().deregisterPersistentSearch(this);
      // Notify any cancellation callbacks.
      for (CancellationCallback callback : cancellationCallbacks)
      // Cancel any other persistent searches which are associated
      // with this one. For example, a persistent search may be
      // distributed across multiple proxies.
      for (PersistentSearch psearch : searchOperation.getClientConnection()
          .getPersistentSearches())
      {
        try
        if (psearch.getMessageID() == getMessageID())
        {
          callback.persistentSearchCancelled(this);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
          cancel(psearch);
        }
      }
    }
@@ -700,6 +754,19 @@
  /**
   * Enable this persistent search. The persistent search will be
   * registered with the client connection and will be prevented from
   * sending responses to the client.
   */
  public void enable()
  {
    searchOperation.getClientConnection().registerPersistentSearch(this);
    searchOperation.setSendResponse(false);
  }
  /**
   * Retrieves a string representation of this persistent search.
   *
   * @return A string representation of this persistent search.
opends/src/server/org/opends/server/core/SearchOperation.java
@@ -334,20 +334,6 @@
  public abstract void setIncludeUsableControl(boolean includeUsableControl);
  /**
   * Register the psearch in the search operation.
   *
   * @param psearch - Persistent search associated to that operation
   */
  public abstract void setPersistentSearch(PersistentSearch psearch);
  /**
   * Get the psearch from the search operation.
   *
   * @return the psearch, or null if no psearch was registered
   */
  public abstract PersistentSearch getPersistentSearch();
  /**
   * Indicates whether the client is able to handle referrals.
   *
   * @return true, if the client is able to handle referrals
opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -143,9 +143,6 @@
  // The matched values control associated with this search operation.
  private MatchedValuesControl matchedValuesControl;
  // The persistent search associated with this search operation.
  private PersistentSearch persistentSearch;
  // The search filter for the search operation.
  private SearchFilter filter;
@@ -249,7 +246,6 @@
    clientAcceptsReferrals = true;
    includeUsableControl   = false;
    responseSent           = new AtomicBoolean(false);
    persistentSearch       = null;
    returnLDAPSubentries   = false;
    matchedValuesControl   = null;
    realAttributesOnly     = false;
@@ -350,7 +346,6 @@
    clientAcceptsReferrals = true;
    includeUsableControl   = false;
    responseSent           = new AtomicBoolean(false);
    persistentSearch       = null;
    returnLDAPSubentries   = false;
    matchedValuesControl   = null;
  }
@@ -1274,12 +1269,6 @@
    if(cancelResult == null && this.cancelRequest == null)
    {
      this.cancelRequest = cancelRequest;
      if (persistentSearch != null)
      {
        persistentSearch.cancel();
        persistentSearch = null;
      }
    }
  }
@@ -1346,14 +1335,6 @@
  /**
   * {@inheritDoc}
   */
  public PersistentSearch getPersistentSearch()
  {
    return persistentSearch;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isIncludeUsableControl()
  {
    return includeUsableControl;
@@ -1370,14 +1351,6 @@
  /**
   * {@inheritDoc}
   */
  public void setPersistentSearch(PersistentSearch psearch)
  {
    this.persistentSearch = psearch;
  }
  /**
   * {@inheritDoc}
   */
  public Long getTimeLimitExpiration()
  {
    return timeLimitExpiration;
opends/src/server/org/opends/server/core/SearchOperationWrapper.java
@@ -332,22 +332,6 @@
  /**
   * {@inheritDoc}
   */
  public void setPersistentSearch(PersistentSearch psearch)
  {
    search.setPersistentSearch(psearch);
  }
  /**
   * {@inheritDoc}
   */
  public PersistentSearch getPersistentSearch()
  {
    return search.getPersistentSearch();
  }
  /**
   * {@inheritDoc}
   */
  public Long getTimeLimitExpiration()
  {
    return search.getTimeLimitExpiration();
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -1287,10 +1287,14 @@
      {
        if (ps.getMessageID() == messageID)
        {
          // We only need to find the first persistent search
          // associated with the provided message ID. The persistent
          // search will ensure that all other related persistent
          // searches are cancelled.
          CancelResult cancelResult = ps.cancel();
          if (keepStats && (cancelResult.getResultCode() ==
              ResultCode.CANCELED))
            ResultCode.CANCELED))
          {
            statTracker.updateAbandonedOperation();
          }
@@ -1428,6 +1432,11 @@
        for (PersistentSearch persistentSearch : getPersistentSearches())
        {
          if (persistentSearch.getMessageID() == messageID)
          {
            continue;
          }
          persistentSearch.cancel();
          lastCompletionTime.set(TimeThread.getTime());
        }
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -231,8 +231,7 @@
      if (persistentSearch != null)
      {
        wfe.registerPersistentSearch(persistentSearch);
        clientConnection.registerPersistentSearch(persistentSearch);
        setSendResponse(false);
        persistentSearch.enable();
      }
@@ -536,7 +535,6 @@
          persistentSearch = new PersistentSearch(this,
                                      psearchControl.getChangeTypes(),
                                      psearchControl.getReturnECs());
          setPersistentSearch(persistentSearch);
          // If we're only interested in changes, then we don't actually want
          // to process the search now.