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

jdemendi
16.02.2007 4a13a6f1bfb01263a8cabce941e94e1e7b883e00
This fix is the refactoring of the compare operation (issue #1886).

- CompareOperation is now an interface
- processing is implemented in
- CompareOperationBasis,
- LocalBackendCompareOperation
- LocalBackendWorkflowElement

As for the other operations, CompareOperationBasis is
wrapped by CompareOperationWrapper. I have also
created an OperationWrapper to implemement the
Operation interface so that other wrappers don't have
to implement the common interface defined by Operation.
OperationWrapper is intended to be subclassed by all the
wrappers.


4 files added
13 files modified
3346 ■■■■■ changed files
opendj-sdk/opends/src/server/org/opends/server/api/AccessControlHandler.java 4 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java 4 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java 1134 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/CompareOperationBasis.java 720 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/CompareOperationWrapper.java 158 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/OperationWrapper.java 511 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/PluginConfigManager.java 12 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java 4 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java 6 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java 86 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 582 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/CompareOperationTestCase.java 94 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxPrivilegeTestCase.java 10 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/api/AccessControlHandler.java
@@ -161,8 +161,8 @@
   * @return  {@code true} if the operation should be allowed by the
   *          access control configuration, or {@code false} if not.
   */
  public abstract boolean isAllowed(CompareOperation
                                         compareOperation);
  public abstract boolean isAllowed(LocalBackendCompareOperation
      compareOperation);
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -880,7 +880,7 @@
     * @param operation The compare operation to check access on.
     * @return  True if access is allowed.
     */
   public boolean isAllowed(CompareOperation operation) {
   public boolean isAllowed(LocalBackendCompareOperation operation) {
       AciLDAPOperationContainer operationContainer =
               new AciLDAPOperationContainer(operation, ACI_COMPARE);
       String baseName;
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -58,7 +58,9 @@
     * @param operation The compare operation to evaluate.
     * @param rights  The rights of a compare operation.
     */
    public AciLDAPOperationContainer(CompareOperation operation, int rights) {
    public AciLDAPOperationContainer(LocalBackendCompareOperation operation,
        int rights)
    {
        super(operation, rights, operation.getEntryToCompare());
    }
opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java
@@ -26,188 +26,27 @@
 */
package org.opends.server.core;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.opends.server.api.Backend;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PostOperationPluginResult;
import org.opends.server.api.plugin.PreOperationPluginResult;
import org.opends.server.api.plugin.PreParsePluginResult;
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.AbstractOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
import org.opends.server.types.CancelRequest;
import org.opends.server.types.CancelResult;
import org.opends.server.types.Control;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.LDAPException;
import org.opends.server.types.LockManager;
import org.opends.server.types.OperationType;
import org.opends.server.types.Privilege;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.operation.PostOperationCompareOperation;
import org.opends.server.types.operation.PostResponseCompareOperation;
import org.opends.server.types.operation.PreOperationCompareOperation;
import org.opends.server.types.operation.PreParseCompareOperation;
import static org.opends.server.core.CoreConstants.*;
import static org.opends.server.loggers.AccessLogger.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugLogger;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.types.Operation;
/**
 * This class defines an operation that may be used to determine whether a
 * This interface defines an operation that may be used to determine whether a
 * specified entry in the Directory Server contains a given attribute-value
 * pair.
 */
public class CompareOperation
       extends AbstractOperation
       implements PreParseCompareOperation, PreOperationCompareOperation,
                  PostOperationCompareOperation, PostResponseCompareOperation
public interface CompareOperation extends Operation
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = DebugLogger.getTracer();
  // The attribute type for this compare operation.
  private AttributeType attributeType;
  // The assertion value for the compare operation.
  private ByteString assertionValue;
  // The raw, unprocessed entry DN as included in the client request.
  private ByteString rawEntryDN;
  // The cancel request that has been issued for this compare operation.
  private CancelRequest cancelRequest;
  // The DN of the entry for the compare operation.
  private DN entryDN;
  // The proxied authorization target DN for this operation.
  private DN proxiedAuthorizationDN;
  // The entry to be compared.
  private Entry entry;
  // The set of response controls for this compare operation.
  private List<Control> responseControls;
  // The attribute type for the compare operation.
  private String rawAttributeType;
  /**
   * Creates a new compare operation with the provided information.
   *
   * @param  clientConnection  The client connection with which this operation
   *                           is associated.
   * @param  operationID       The operation ID for this operation.
   * @param  messageID         The message ID of the request with which this
   *                           operation is associated.
   * @param  requestControls   The set of controls included in the request.
   * @param  rawEntryDN        The raw, unprocessed entry DN as provided in the
   *                           client request.  This may or may not be a valid
   *                           DN as no validation will have been performed yet.
   * @param  rawAttributeType  The raw attribute type for the compare operation.
   * @param  assertionValue    The assertion value for the compare operation.
   */
  public CompareOperation(ClientConnection clientConnection, long operationID,
                          int messageID, List<Control> requestControls,
                          ByteString rawEntryDN, String rawAttributeType,
                          ByteString assertionValue)
  {
    super(clientConnection, operationID, messageID, requestControls);
    this.rawEntryDN       = rawEntryDN;
    this.rawAttributeType = rawAttributeType;
    this.assertionValue   = assertionValue;
    responseControls       = new ArrayList<Control>();
    entry                  = null;
    entryDN                = null;
    attributeType          = null;
    cancelRequest          = null;
    proxiedAuthorizationDN = null;
  }
  /**
   * Creates a new compare operation with the provided information.
   *
   * @param  clientConnection  The client connection with which this operation
   *                           is associated.
   * @param  operationID       The operation ID for this operation.
   * @param  messageID         The message ID of the request with which this
   *                           operation is associated.
   * @param  requestControls   The set of controls included in the request.
   * @param  entryDN           The entry DN for this compare operation.
   * @param  attributeType     The attribute type for this compare operation.
   * @param  assertionValue    The assertion value for the compare operation.
   */
  public CompareOperation(ClientConnection clientConnection, long operationID,
                          int messageID, List<Control> requestControls,
                          DN entryDN, AttributeType attributeType,
                          ByteString assertionValue)
  {
    super(clientConnection, operationID, messageID, requestControls);
    this.entryDN        = entryDN;
    this.attributeType  = attributeType;
    this.assertionValue = assertionValue;
    responseControls       = new ArrayList<Control>();
    rawEntryDN             = new ASN1OctetString(entryDN.toString());
    rawAttributeType       = attributeType.getNameOrOID();
    cancelRequest          = null;
    entry                  = null;
    proxiedAuthorizationDN = null;
  }
  /**
   * Retrieves the raw, unprocessed entry DN as included in the client request.
   * The DN that is returned may or may not be a valid DN, since no validation
   * will have been performed upon it.
   *
   * @return  The raw, unprocessed entry DN as included in the client request.
   */
  public final ByteString getRawEntryDN()
  {
    return rawEntryDN;
  }
  public ByteString getRawEntryDN();
  /**
@@ -217,13 +56,7 @@
   * @param  rawEntryDN  The raw, unprocessed entry DN as included in the client
   *                     request.
   */
  public final void setRawEntryDN(ByteString rawEntryDN)
  {
    this.rawEntryDN = rawEntryDN;
    entryDN = null;
  }
  public void setRawEntryDN(ByteString rawEntryDN);
  /**
@@ -234,11 +67,7 @@
   * @return  The DN of the entry to compare, or <CODE>null</CODE> if the raw
   *          entry DN has not yet been processed.
   */
  public final DN getEntryDN()
  {
    return entryDN;
  }
  public DN getEntryDN();
  /**
@@ -246,11 +75,7 @@
   *
   * @return  The raw attribute type for this compare operation.
   */
  public final String getRawAttributeType()
  {
    return rawAttributeType;
  }
  public String getRawAttributeType();
  /**
@@ -260,13 +85,7 @@
   * @param  rawAttributeType  The raw attribute type for this compare
   *                           operation.
   */
  public final void setRawAttributeType(String rawAttributeType)
  {
    this.rawAttributeType = rawAttributeType;
    attributeType = null;
  }
  public void setRawAttributeType(String rawAttributeType);
  /**
@@ -276,23 +95,23 @@
   *
   * @return  The attribute type for this compare operation.
   */
  public final AttributeType getAttributeType()
  {
    return attributeType;
  }
  public AttributeType getAttributeType();
  /**
   * Specifies the attribute type for this compare operation.
   *
   * @param attributeType  The attribute type for this compare operation.
   */
  public void setAttributeType(AttributeType attributeType);
  /**
   * Retrieves the assertion value for this compare operation.
   *
   * @return  The assertion value for this compare operation.
   */
  public final ByteString getAssertionValue()
  {
    return assertionValue;
  }
  public ByteString getAssertionValue();
  /**
@@ -301,926 +120,23 @@
   *
   * @param  assertionValue  The assertion value for this compare operation.
   */
  public final void setAssertionValue(ByteString assertionValue)
  {
    this.assertionValue = assertionValue;
  }
  public void setAssertionValue(ByteString assertionValue);
  /**
   * Retrieves the entry to target with the compare operation.  This should not
   * be called by pre-parse plugins.
   * Retrieves the proxied authorization target DN for this compare operation.
   *
   * @return  The entry to target with the compare operation, or
   *          <CODE>null</CODE> if the entry is not yet available.
   * @return  The proxied authorization target DN for this compare operation
   */
  public final Entry getEntryToCompare()
  {
    return entry;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final OperationType getOperationType()
  {
    // Note that no debugging will be done in this method because it is a likely
    // candidate for being called by the logging subsystem.
    return OperationType.COMPARE;
  }
  public DN getProxiedAuthorizationDN();
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void disconnectClient(DisconnectReason disconnectReason,
                                     boolean sendNotification, String message,
                                     int messageID)
  {
    // Before calling clientConnection.disconnect, we need to mark this
    // operation as cancelled so that the attempt to cancel it later won't cause
    // an unnecessary delay.
    setCancelResult(CancelResult.CANCELED);
    clientConnection.disconnect(disconnectReason, sendNotification, message,
                                messageID);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final String[][] getRequestLogElements()
  {
    // Note that no debugging will be done in this method because it is a likely
    // candidate for being called by the logging subsystem.
    return new String[][]
    {
      new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) },
      new String[] { LOG_ELEMENT_COMPARE_ATTR, rawAttributeType }
    };
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final String[][] getResponseLogElements()
  {
    // Note that no debugging will be done in this method because it is a likely
    // candidate for being called by the logging subsystem.
    String resultCode = String.valueOf(getResultCode().getIntValue());
    String errorMessage;
    StringBuilder errorMessageBuffer = getErrorMessage();
    if (errorMessageBuffer == null)
    {
      errorMessage = null;
    }
    else
    {
      errorMessage = errorMessageBuffer.toString();
    }
    String matchedDNStr;
    DN matchedDN = getMatchedDN();
    if (matchedDN == null)
    {
      matchedDNStr = null;
    }
    else
    {
      matchedDNStr = matchedDN.toString();
    }
    String referrals;
    List<String> referralURLs = getReferralURLs();
    if ((referralURLs == null) || referralURLs.isEmpty())
    {
      referrals = null;
    }
    else
    {
      StringBuilder buffer = new StringBuilder();
      Iterator<String> iterator = referralURLs.iterator();
      buffer.append(iterator.next());
      while (iterator.hasNext())
      {
        buffer.append(", ");
        buffer.append(iterator.next());
      }
      referrals = buffer.toString();
    }
    String processingTime =
         String.valueOf(getProcessingTime());
    return new String[][]
    {
      new String[] { LOG_ELEMENT_RESULT_CODE, resultCode },
      new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage },
      new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr },
      new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals },
      new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime }
    };
  }
  /**
   * Retrieves the proxied authorization DN for this operation if proxied
   * authorization has been requested.
   * Specifies the proxied authorization target DN for this compare operation.
   *
   * @return  The proxied authorization DN for this operation if proxied
   *          authorization has been requested, or {@code null} if proxied
   *          authorization has not been requested.
   * @param proxiedAuthorizationDN  The proxied authorization target DN for
   *                                this compare operation
   */
  public DN getProxiedAuthorizationDN()
  {
    return proxiedAuthorizationDN;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final List<Control> getResponseControls()
  {
    return responseControls;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void addResponseControl(Control control)
  {
    responseControls.add(control);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void removeResponseControl(Control control)
  {
    responseControls.remove(control);
  }
  /**
   * Performs the work of actually processing this operation.  This
   * should include all processing for the operation, including
   * invoking plugins, logging messages, performing access control,
   * managing synchronization, and any other work that might need to
   * be done in the course of processing.
   */
  public final void run()
  {
    setResultCode(ResultCode.UNDEFINED);
    // Get the plugin config manager that will be used for invoking plugins.
    PluginConfigManager pluginConfigManager =
         DirectoryServer.getPluginConfigManager();
    boolean skipPostOperation = false;
    // Start the processing timer.
    setProcessingStartTime();
    // Check for and handle a request to cancel this operation.
    if (cancelRequest != null)
    {
      indicateCancelled(cancelRequest);
      setProcessingStopTime();
      return;
    }
    // Create a labeled block of code that we can break out of if a problem is
    // detected.
compareProcessing:
    {
      // Invoke the pre-parse compare plugins.
      PreParsePluginResult preParseResult =
           pluginConfigManager.invokePreParseComparePlugins(this);
      if (preParseResult.connectionTerminated())
      {
        // There's no point in continuing with anything.  Log the request and
        // result and return.
        setResultCode(ResultCode.CANCELED);
        int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT;
        appendErrorMessage(getMessage(msgID));
        setProcessingStopTime();
        logCompareRequest(this);
        logCompareResponse(this);
        pluginConfigManager.invokePostResponseComparePlugins(this);
        return;
      }
      else if (preParseResult.sendResponseImmediately())
      {
        skipPostOperation = true;
        logCompareRequest(this);
        break compareProcessing;
      }
      else if (preParseResult.skipCoreProcessing())
      {
        skipPostOperation = false;
        break compareProcessing;
      }
      // Log the compare request message.
      logCompareRequest(this);
      // Check for and handle a request to cancel this operation.
      if (cancelRequest != null)
      {
        indicateCancelled(cancelRequest);
        setProcessingStopTime();
        logCompareResponse(this);
        pluginConfigManager.invokePostResponseComparePlugins(this);
        return;
      }
      // Process the entry DN to convert it from the raw form to the form
      // required for the rest of the compare processing.
      try
      {
        if (entryDN == null)
        {
          entryDN = DN.decode(rawEntryDN);
        }
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        }
        setResultCode(de.getResultCode());
        appendErrorMessage(de.getErrorMessage());
        skipPostOperation = true;
        break compareProcessing;
      }
      // If the target entry is in the server configuration, then make sure the
      // requester has the CONFIG_READ privilege.
      if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) &&
          (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, this)))
      {
        int msgID = MSGID_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES;
        appendErrorMessage(getMessage(msgID));
        setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
        skipPostOperation = true;
        break compareProcessing;
      }
      // Check for and handle a request to cancel this operation.
      if (cancelRequest != null)
      {
        indicateCancelled(cancelRequest);
        setProcessingStopTime();
        logCompareResponse(this);
        pluginConfigManager.invokePostResponseComparePlugins(this);
        return;
      }
      // Grab a read lock on the entry.
      Lock readLock = null;
      for (int i=0; i < 3; i++)
      {
        readLock = LockManager.lockRead(entryDN);
        if (readLock != null)
        {
          break;
        }
      }
      if (readLock == null)
      {
        int    msgID   = MSGID_COMPARE_CANNOT_LOCK_ENTRY;
        String message = getMessage(msgID, String.valueOf(entryDN));
        setResultCode(DirectoryServer.getServerErrorResultCode());
        appendErrorMessage(message);
        skipPostOperation = true;
        break compareProcessing;
      }
      try
      {
        // Get the entry.  If it does not exist, then fail.
        try
        {
          entry = DirectoryServer.getEntry(entryDN);
          if (entry == null)
          {
            setResultCode(ResultCode.NO_SUCH_OBJECT);
            appendErrorMessage(getMessage(MSGID_COMPARE_NO_SUCH_ENTRY,
                                          String.valueOf(entryDN)));
            // See if one of the entry's ancestors exists.
            DN parentDN = entryDN.getParentDNInSuffix();
            while (parentDN != null)
            {
              try
              {
                if (DirectoryServer.entryExists(parentDN))
                {
                  setMatchedDN(parentDN);
                  break;
                }
              }
              catch (Exception e)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
                }
                break;
              }
              parentDN = parentDN.getParentDNInSuffix();
            }
            break compareProcessing;
          }
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, de);
          }
          setResultCode(de.getResultCode());
          appendErrorMessage(de.getErrorMessage());
          break compareProcessing;
        }
        // Check to see if there are any controls in the request.  If so, then
        // see if there is any special processing required.
        List<Control> requestControls = getRequestControls();
        if ((requestControls != null) && (! requestControls.isEmpty()))
        {
          for (int i=0; i < requestControls.size(); i++)
          {
            Control c   = requestControls.get(i);
            String  oid = c.getOID();
            if (oid.equals(OID_LDAP_ASSERTION))
            {
              LDAPAssertionRequestControl assertControl;
              if (c instanceof LDAPAssertionRequestControl)
              {
                assertControl = (LDAPAssertionRequestControl) c;
              }
              else
              {
                try
                {
                  assertControl = LDAPAssertionRequestControl.decodeControl(c);
                  requestControls.set(i, assertControl);
                }
                catch (LDAPException le)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
                  }
                  setResultCode(ResultCode.valueOf(le.getResultCode()));
                  appendErrorMessage(le.getMessage());
                  break compareProcessing;
                }
              }
              try
              {
                // FIXME -- We need to determine whether the current user has
                //          permission to make this determination.
                SearchFilter filter = assertControl.getSearchFilter();
                if (! filter.matchesEntry(entry))
                {
                  setResultCode(ResultCode.ASSERTION_FAILED);
                  appendErrorMessage(getMessage(MSGID_COMPARE_ASSERTION_FAILED,
                                                String.valueOf(entryDN)));
                  break compareProcessing;
                }
              }
              catch (DirectoryException de)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
                }
                setResultCode(ResultCode.PROTOCOL_ERROR);
                int msgID = MSGID_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
                                              de.getErrorMessage()));
                break compareProcessing;
              }
            }
            else if (oid.equals(OID_PROXIED_AUTH_V1))
            {
              // The requester must have the PROXIED_AUTH privilige in order to
              // be able to use this control.
              if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
              {
                int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
                appendErrorMessage(getMessage(msgID));
                setResultCode(ResultCode.AUTHORIZATION_DENIED);
                break compareProcessing;
              }
              ProxiedAuthV1Control proxyControl;
              if (c instanceof ProxiedAuthV1Control)
              {
                proxyControl = (ProxiedAuthV1Control) c;
              }
              else
              {
                try
                {
                  proxyControl = ProxiedAuthV1Control.decodeControl(c);
                }
                catch (LDAPException le)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
                  }
                  setResultCode(ResultCode.valueOf(le.getResultCode()));
                  appendErrorMessage(le.getMessage());
                  break compareProcessing;
                }
              }
              Entry authorizationEntry;
              try
              {
                authorizationEntry = proxyControl.getAuthorizationEntry();
              }
              catch (DirectoryException de)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
                }
                setResultCode(de.getResultCode());
                appendErrorMessage(de.getErrorMessage());
                break compareProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                 authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break compareProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
              if (authorizationEntry == null)
              {
                proxiedAuthorizationDN = DN.nullDN();
              }
              else
              {
                proxiedAuthorizationDN = authorizationEntry.getDN();
              }
            }
            else if (oid.equals(OID_PROXIED_AUTH_V2))
            {
              // The requester must have the PROXIED_AUTH privilige in order to
              // be able to use this control.
              if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
              {
                int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
                appendErrorMessage(getMessage(msgID));
                setResultCode(ResultCode.AUTHORIZATION_DENIED);
                break compareProcessing;
              }
              ProxiedAuthV2Control proxyControl;
              if (c instanceof ProxiedAuthV2Control)
              {
                proxyControl = (ProxiedAuthV2Control) c;
              }
              else
              {
                try
                {
                  proxyControl = ProxiedAuthV2Control.decodeControl(c);
                }
                catch (LDAPException le)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
                  }
                  setResultCode(ResultCode.valueOf(le.getResultCode()));
                  appendErrorMessage(le.getMessage());
                  break compareProcessing;
                }
              }
              Entry authorizationEntry;
              try
              {
                authorizationEntry = proxyControl.getAuthorizationEntry();
              }
              catch (DirectoryException de)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
                }
                setResultCode(de.getResultCode());
                appendErrorMessage(de.getErrorMessage());
                break compareProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                 authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break compareProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
              if (authorizationEntry == null)
              {
                proxiedAuthorizationDN = DN.nullDN();
              }
              else
              {
                proxiedAuthorizationDN = authorizationEntry.getDN();
              }
            }
            // NYI -- Add support for additional controls.
            else if (c.isCritical())
            {
              Backend backend = DirectoryServer.getBackend(entryDN);
              if ((backend == null) || (! backend.supportsControl(oid)))
              {
                setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
                int msgID = MSGID_COMPARE_UNSUPPORTED_CRITICAL_CONTROL;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
                                              oid));
                break compareProcessing;
              }
            }
          }
        }
        // Check to see if the client has permission to perform the
        // compare.
        // FIXME: for now assume that this will check all permission
        // pertinent to the operation. This includes proxy authorization
        // and any other controls specified.
        // FIXME: earlier checks to see if the entry already exists may
        // have already exposed sensitive information to the client.
        if (AccessControlConfigManager.getInstance()
            .getAccessControlHandler().isAllowed(this) == false) {
          setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
          int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
          appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
          skipPostOperation = true;
          break compareProcessing;
        }
        // Check for and handle a request to cancel this operation.
        if (cancelRequest != null)
        {
          indicateCancelled(cancelRequest);
          setProcessingStopTime();
          logCompareResponse(this);
          pluginConfigManager.invokePostResponseComparePlugins(this);
          return;
        }
        // Invoke the pre-operation compare plugins.
        PreOperationPluginResult preOpResult =
             pluginConfigManager.invokePreOperationComparePlugins(this);
        if (preOpResult.connectionTerminated())
        {
          // There's no point in continuing with anything.  Log the request and
          // result and return.
          setResultCode(ResultCode.CANCELED);
          int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT;
          appendErrorMessage(getMessage(msgID));
          setProcessingStopTime();
          logCompareResponse(this);
          pluginConfigManager.invokePostResponseComparePlugins(this);
          return;
        }
        else if (preOpResult.sendResponseImmediately())
        {
          skipPostOperation = true;
          break compareProcessing;
        }
        else if (preOpResult.skipCoreProcessing())
        {
          skipPostOperation = false;
          break compareProcessing;
        }
        // Get the base attribute type and set of options.
        String          baseName;
        HashSet<String> options;
        int             semicolonPos = rawAttributeType.indexOf(';');
        if (semicolonPos > 0)
        {
          baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos));
          options = new HashSet<String>();
          int nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
          while (nextPos > 0)
          {
            options.add(rawAttributeType.substring(semicolonPos+1, nextPos));
            semicolonPos = nextPos;
            nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
          }
          options.add(rawAttributeType.substring(semicolonPos+1));
        }
        else
        {
          baseName = toLowerCase(rawAttributeType);
          options  = null;
        }
        // Actually perform the compare operation.
        List<Attribute> attrList = null;
        if (attributeType == null)
        {
          attributeType = DirectoryServer.getAttributeType(baseName);
        }
        if (attributeType == null)
        {
          attrList = entry.getAttribute(baseName, options);
          attributeType = DirectoryServer.getDefaultAttributeType(baseName);
        }
        else
        {
          attrList = entry.getAttribute(attributeType, options);
        }
        if ((attrList == null) || attrList.isEmpty())
        {
          setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
          if (options == null)
          {
            appendErrorMessage(getMessage(MSGID_COMPARE_OP_NO_SUCH_ATTR,
                                          String.valueOf(entryDN), baseName));
          }
          else
          {
            appendErrorMessage(getMessage(
                                    MSGID_COMPARE_OP_NO_SUCH_ATTR_WITH_OPTIONS,
                                    String.valueOf(entryDN), baseName));
          }
        }
        else
        {
          AttributeValue value = new AttributeValue(attributeType,
                                                    assertionValue);
          boolean matchFound = false;
          for (Attribute a : attrList)
          {
            if (a.hasValue(value))
            {
              matchFound = true;
              break;
            }
          }
          if (matchFound)
          {
            setResultCode(ResultCode.COMPARE_TRUE);
          }
          else
          {
            setResultCode(ResultCode.COMPARE_FALSE);
          }
        }
      }
      finally
      {
        LockManager.unlock(entryDN, readLock);
      }
    }
    // Check for and handle a request to cancel this operation.
    if (cancelRequest != null)
    {
      indicateCancelled(cancelRequest);
      setProcessingStopTime();
      logCompareResponse(this);
      pluginConfigManager.invokePostResponseComparePlugins(this);
      return;
    }
    // Invoke the post-operation compare plugins.
    if (! skipPostOperation)
    {
      PostOperationPluginResult postOperationResult =
           pluginConfigManager.invokePostOperationComparePlugins(this);
      if (postOperationResult.connectionTerminated())
      {
        setResultCode(ResultCode.CANCELED);
        int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT;
        appendErrorMessage(getMessage(msgID));
        setProcessingStopTime();
        logCompareResponse(this);
        pluginConfigManager.invokePostResponseComparePlugins(this);
        return;
      }
    }
    // Indicate that it is now too late to attempt to cancel the operation.
    setCancelResult(CancelResult.TOO_LATE);
    // Stop the processing timer.
    setProcessingStopTime();
    // Send the compare response to the client.
    clientConnection.sendResponse(this);
    // Log the compare response message.
    logCompareResponse(this);
    // Invoke the post-response compare plugins.
    pluginConfigManager.invokePostResponseComparePlugins(this);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final CancelResult cancel(CancelRequest cancelRequest)
  {
    this.cancelRequest = cancelRequest;
    CancelResult cancelResult = getCancelResult();
    long stopWaitingTime = System.currentTimeMillis() + 5000;
    while ((cancelResult == null) &&
           (System.currentTimeMillis() < stopWaitingTime))
    {
      try
      {
        Thread.sleep(50);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
      }
      cancelResult = getCancelResult();
    }
    if (cancelResult == null)
    {
      // This can happen in some rare cases (e.g., if a client disconnects and
      // there is still a lot of data to send to that client), and in this case
      // we'll prevent the cancel thread from blocking for a long period of
      // time.
      cancelResult = CancelResult.CANNOT_CANCEL;
    }
    return cancelResult;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final CancelRequest getCancelRequest()
  {
    return cancelRequest;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean setCancelRequest(CancelRequest cancelRequest)
  {
    this.cancelRequest = cancelRequest;
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void toString(StringBuilder buffer)
  {
    buffer.append("CompareOperation(connID=");
    buffer.append(clientConnection.getConnectionID());
    buffer.append(", opID=");
    buffer.append(operationID);
    buffer.append(", dn=");
    buffer.append(rawEntryDN);
    buffer.append(", attr=");
    buffer.append(rawAttributeType);
    buffer.append(")");
  }
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN);
}
opendj-sdk/opends/src/server/org/opends/server/core/CompareOperationBasis.java
New file
@@ -0,0 +1,720 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import static org.opends.server.core.CoreConstants.*;
import static org.opends.server.loggers.AccessLogger.logCompareRequest;
import static org.opends.server.loggers.AccessLogger.logCompareResponse;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PreParsePluginResult;
import org.opends.server.loggers.debug.DebugLogger;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.AbstractOperation;
import org.opends.server.types.AttributeType;
import org.opends.server.types.ByteString;
import org.opends.server.types.CancelRequest;
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.DisconnectReason;
import org.opends.server.types.Entry;
import org.opends.server.types.Operation;
import org.opends.server.types.OperationType;
import org.opends.server.types.ResultCode;
import org.opends.server.types.operation.PostResponseCompareOperation;
import org.opends.server.types.operation.PreParseCompareOperation;
import org.opends.server.workflowelement.localbackend.
       LocalBackendCompareOperation;
/**
 * This class defines an operation that may be used to determine whether a
 * specified entry in the Directory Server contains a given attribute-value
 * pair.
 */
public class CompareOperationBasis
             extends AbstractOperation
             implements PreParseCompareOperation, CompareOperation,
                        Runnable, PostResponseCompareOperation
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = DebugLogger.getTracer();
  // The attribute type for this compare operation.
  private AttributeType attributeType;
  // The assertion value for the compare operation.
  private ByteString assertionValue;
  // The raw, unprocessed entry DN as included in the client request.
  private ByteString rawEntryDN;
  // The cancel request that has been issued for this compare operation.
  private CancelRequest cancelRequest;
  // The DN of the entry for the compare operation.
  private DN entryDN;
  // The proxied authorization target DN for this operation.
  private DN proxiedAuthorizationDN;
  // The set of response controls for this compare operation.
  private List<Control> responseControls;
  // The attribute type for the compare operation.
  private String rawAttributeType;
  /**
   * Creates a new compare operation with the provided information.
   *
   * @param  clientConnection  The client connection with which this operation
   *                           is associated.
   * @param  operationID       The operation ID for this operation.
   * @param  messageID         The message ID of the request with which this
   *                           operation is associated.
   * @param  requestControls   The set of controls included in the request.
   * @param  rawEntryDN        The raw, unprocessed entry DN as provided in the
   *                           client request.  This may or may not be a valid
   *                           DN as no validation will have been performed yet.
   * @param  rawAttributeType  The raw attribute type for the compare operation.
   * @param  assertionValue    The assertion value for the compare operation.
   */
  public CompareOperationBasis(
                          ClientConnection clientConnection, long operationID,
                          int messageID, List<Control> requestControls,
                          ByteString rawEntryDN, String rawAttributeType,
                          ByteString assertionValue)
  {
    super(clientConnection, operationID, messageID, requestControls);
    this.rawEntryDN       = rawEntryDN;
    this.rawAttributeType = rawAttributeType;
    this.assertionValue   = assertionValue;
    responseControls       = new ArrayList<Control>();
    entryDN                = null;
    attributeType          = null;
    cancelRequest          = null;
    proxiedAuthorizationDN = null;
  }
  /**
   * Creates a new compare operation with the provided information.
   *
   * @param  clientConnection  The client connection with which this operation
   *                           is associated.
   * @param  operationID       The operation ID for this operation.
   * @param  messageID         The message ID of the request with which this
   *                           operation is associated.
   * @param  requestControls   The set of controls included in the request.
   * @param  entryDN           The entry DN for this compare operation.
   * @param  attributeType     The attribute type for this compare operation.
   * @param  assertionValue    The assertion value for the compare operation.
   */
  public CompareOperationBasis(
                          ClientConnection clientConnection, long operationID,
                          int messageID, List<Control> requestControls,
                          DN entryDN, AttributeType attributeType,
                          ByteString assertionValue)
  {
    super(clientConnection, operationID, messageID, requestControls);
    this.entryDN        = entryDN;
    this.attributeType  = attributeType;
    this.assertionValue = assertionValue;
    responseControls       = new ArrayList<Control>();
    rawEntryDN             = new ASN1OctetString(entryDN.toString());
    rawAttributeType       = attributeType.getNameOrOID();
    cancelRequest          = null;
    proxiedAuthorizationDN = null;
  }
  /**
   * {@inheritDoc}
   */
  public final ByteString getRawEntryDN()
  {
    return rawEntryDN;
  }
  /**
   * {@inheritDoc}
   */
  public final void setRawEntryDN(ByteString rawEntryDN)
  {
    this.rawEntryDN = rawEntryDN;
    entryDN = null;
  }
  /**
   * {@inheritDoc}
   */
  public final DN getEntryDN()
  {
    return entryDN;
  }
  /**
   * {@inheritDoc}
   */
  public final String getRawAttributeType()
  {
    return rawAttributeType;
  }
  /**
   * {@inheritDoc}
   */
  public final void setRawAttributeType(String rawAttributeType)
  {
    this.rawAttributeType = rawAttributeType;
    attributeType = null;
  }
  /**
   * {@inheritDoc}
   */
  public final AttributeType getAttributeType()
  {
    return attributeType;
  }
  /**
   * {@inheritDoc}
   */
  public void setAttributeType(AttributeType attributeType)
  {
    this.attributeType = attributeType;
  }
  /**
   * {@inheritDoc}
   */
  public final ByteString getAssertionValue()
  {
    return assertionValue;
  }
  /**
   * {@inheritDoc}
   */
  public final void setAssertionValue(ByteString assertionValue)
  {
    this.assertionValue = assertionValue;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final OperationType getOperationType()
  {
    // Note that no debugging will be done in this method because it is a likely
    // candidate for being called by the logging subsystem.
    return OperationType.COMPARE;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void disconnectClient(DisconnectReason disconnectReason,
                                     boolean sendNotification, String message,
                                     int messageID)
  {
    // Before calling clientConnection.disconnect, we need to mark this
    // operation as cancelled so that the attempt to cancel it later won't cause
    // an unnecessary delay.
    setCancelResult(CancelResult.CANCELED);
    clientConnection.disconnect(disconnectReason, sendNotification, message,
                                messageID);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final String[][] getRequestLogElements()
  {
    // Note that no debugging will be done in this method because it is a likely
    // candidate for being called by the logging subsystem.
    return new String[][]
    {
      new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) },
      new String[] { LOG_ELEMENT_COMPARE_ATTR, rawAttributeType }
    };
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final String[][] getResponseLogElements()
  {
    // Note that no debugging will be done in this method because it is a likely
    // candidate for being called by the logging subsystem.
    String resultCode = String.valueOf(getResultCode().getIntValue());
    String errorMessage;
    StringBuilder errorMessageBuffer = getErrorMessage();
    if (errorMessageBuffer == null)
    {
      errorMessage = null;
    }
    else
    {
      errorMessage = errorMessageBuffer.toString();
    }
    String matchedDNStr;
    DN matchedDN = getMatchedDN();
    if (matchedDN == null)
    {
      matchedDNStr = null;
    }
    else
    {
      matchedDNStr = matchedDN.toString();
    }
    String referrals;
    List<String> referralURLs = getReferralURLs();
    if ((referralURLs == null) || referralURLs.isEmpty())
    {
      referrals = null;
    }
    else
    {
      StringBuilder buffer = new StringBuilder();
      Iterator<String> iterator = referralURLs.iterator();
      buffer.append(iterator.next());
      while (iterator.hasNext())
      {
        buffer.append(", ");
        buffer.append(iterator.next());
      }
      referrals = buffer.toString();
    }
    String processingTime =
         String.valueOf(getProcessingTime());
    return new String[][]
    {
      new String[] { LOG_ELEMENT_RESULT_CODE, resultCode },
      new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage },
      new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr },
      new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals },
      new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime }
    };
  }
  /**
   * Retrieves the proxied authorization DN for this operation if proxied
   * authorization has been requested.
   *
   * @return  The proxied authorization DN for this operation if proxied
   *          authorization has been requested, or {@code null} if proxied
   *          authorization has not been requested.
   */
  public DN getProxiedAuthorizationDN()
  {
    return proxiedAuthorizationDN;
  }
  /**
   * {@inheritDoc}
   */
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    this.proxiedAuthorizationDN = proxiedAuthorizationDN;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final List<Control> getResponseControls()
  {
    return responseControls;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void addResponseControl(Control control)
  {
    responseControls.add(control);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void removeResponseControl(Control control)
  {
    responseControls.remove(control);
  }
  /**
   * Performs the work of actually processing this operation.  This
   * should include all processing for the operation, including
   * invoking plugins, logging messages, performing access control,
   * managing synchronization, and any other work that might need to
   * be done in the course of processing.
   */
  public final void run()
  {
    setResultCode(ResultCode.UNDEFINED);
    // Start the processing timer.
    setProcessingStartTime();
    // Check for and handle a request to cancel this operation.
    if (cancelRequest != null)
    {
      indicateCancelled(cancelRequest);
      setProcessingStopTime();
      return;
    }
    // Get the plugin config manager that will be used for invoking plugins.
    PluginConfigManager pluginConfigManager =
         DirectoryServer.getPluginConfigManager();
    // Create a labeled block of code that we can break out of if a problem is
    // detected.
    boolean workflowExecuted = false;
compareProcessing:
    {
      // Invoke the pre-parse compare plugins.
      PreParsePluginResult preParseResult =
           pluginConfigManager.invokePreParseComparePlugins(this);
      if (preParseResult.connectionTerminated())
      {
        // There's no point in continuing with anything.  Log the request and
        // result and return.
        setResultCode(ResultCode.CANCELED);
        int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT;
        appendErrorMessage(getMessage(msgID));
        setProcessingStopTime();
        logCompareRequest(this);
        logCompareResponse(this);
        pluginConfigManager.invokePostResponseComparePlugins(this);
        return;
      }
      else if (preParseResult.sendResponseImmediately())
      {
        logCompareRequest(this);
        break compareProcessing;
      }
      else if (preParseResult.skipCoreProcessing())
      {
        break compareProcessing;
      }
      // Log the compare request message.
      logCompareRequest(this);
      // Check for and handle a request to cancel this operation.
      if (cancelRequest != null)
      {
        indicateCancelled(cancelRequest);
        setProcessingStopTime();
        logCompareResponse(this);
        pluginConfigManager.invokePostResponseComparePlugins(this);
        return;
      }
      // Process the entry DN to convert it from the raw form to the form
      // required for the rest of the compare processing.
      try
      {
        if (entryDN == null)
        {
          entryDN = DN.decode(rawEntryDN);
        }
      }
      catch (DirectoryException de)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, de);
        }
        setResultCode(de.getResultCode());
        appendErrorMessage(de.getErrorMessage());
        break compareProcessing;
      }
      // Retrieve the network group registered with the client connection
      // and get a workflow to process the operation.
      NetworkGroup ng = getClientConnection().getNetworkGroup();
      Workflow workflow = ng.getWorkflowCandidate(entryDN);
      if (workflow == null)
      {
        // We have found no workflow for the requested base DN, just return
        // a no such entry result code and stop the processing.
        updateOperationErrMsgAndResCode();
        break compareProcessing;
      }
      workflow.execute(this);
      workflowExecuted = true;
    }
    // Check for and handle a request to cancel this operation.
    if (cancelRequest != null)
    {
      indicateCancelled(cancelRequest);
      setProcessingStopTime();
      logCompareResponse(this);
      pluginConfigManager.invokePostResponseComparePlugins(this);
      return;
    }
    // Indicate that it is now too late to attempt to cancel the operation.
    setCancelResult(CancelResult.TOO_LATE);
    // Stop the processing timer.
    setProcessingStopTime();
    // Send the compare response to the client unless the operation result
    // code is CANCELED
    if (getResultCode() != ResultCode.CANCELED)
    {
      clientConnection.sendResponse(this);
    }
    // Log the compare response message.
    logCompareResponse(this);
    // Invoke the post-response compare plugins.
    if (workflowExecuted)
    {
      List localOperations =
        (List)getAttachment(Operation.LOCALBACKENDOPERATIONS);
      if (localOperations != null)
      {
        for (Object localOp : localOperations)
        {
          LocalBackendCompareOperation localOperation =
            (LocalBackendCompareOperation)localOp;
          pluginConfigManager.invokePostResponseComparePlugins(localOperation);
        }
      }
    }
    else
    {
      pluginConfigManager.invokePostResponseComparePlugins(this);
    }
  }
  /**
   * Updates the error message and the result code of the operation.
   *
   * This method is called because no workflow was found to process
   * the operation.
   */
  private void updateOperationErrMsgAndResCode()
  {
    setResultCode(ResultCode.NO_SUCH_OBJECT);
    appendErrorMessage(
      getMessage(MSGID_COMPARE_NO_SUCH_ENTRY, String.valueOf(getEntryDN())));
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final CancelResult cancel(CancelRequest cancelRequest)
  {
    this.cancelRequest = cancelRequest;
    CancelResult cancelResult = getCancelResult();
    long stopWaitingTime = System.currentTimeMillis() + 5000;
    while ((cancelResult == null) &&
           (System.currentTimeMillis() < stopWaitingTime))
    {
      try
      {
        Thread.sleep(50);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
      }
      cancelResult = getCancelResult();
    }
    if (cancelResult == null)
    {
      // This can happen in some rare cases (e.g., if a client disconnects and
      // there is still a lot of data to send to that client), and in this case
      // we'll prevent the cancel thread from blocking for a long period of
      // time.
      cancelResult = CancelResult.CANNOT_CANCEL;
    }
    return cancelResult;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final CancelRequest getCancelRequest()
  {
    return cancelRequest;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean setCancelRequest(CancelRequest cancelRequest)
  {
    this.cancelRequest = cancelRequest;
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public final void toString(StringBuilder buffer)
  {
    buffer.append("CompareOperation(connID=");
    buffer.append(clientConnection.getConnectionID());
    buffer.append(", opID=");
    buffer.append(operationID);
    buffer.append(", dn=");
    buffer.append(rawEntryDN);
    buffer.append(", attr=");
    buffer.append(rawAttributeType);
    buffer.append(")");
  }
  /**
   * {@inheritDoc}
   *
   * This method always returns null.
   */
  public Entry getEntryToCompare()
  {
    return null;
  }
}
opendj-sdk/opends/src/server/org/opends/server/core/CompareOperationWrapper.java
New file
@@ -0,0 +1,158 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import org.opends.server.types.AttributeType;
import org.opends.server.types.ByteString;
import org.opends.server.types.DN;
/**
 * This abstract class wraps/decorates a given compare operation.
 * This class will be extended by sub-classes to enhance the
 * functionnality of the CompareOperationBasis.
 */
public abstract class CompareOperationWrapper
  extends OperationWrapper
  implements CompareOperation
{
  // The wrapped operation
  private CompareOperation compare;
  /**
   * Creates a new compare operation based on the provided compare operation.
   *
   * @param compare The compare operation to wrap
   */
  public CompareOperationWrapper(CompareOperation compare)
  {
    super(compare);
    this.compare = compare;
  }
  /**
   * {@inheritDoc}
   */
  public ByteString getRawEntryDN()
  {
    return compare.getRawEntryDN();
  }
  /**
   * {@inheritDoc}
   */
  public void setRawEntryDN(ByteString rawEntryDN)
  {
    compare.setRawEntryDN(rawEntryDN);
  }
  /**
   * {@inheritDoc}
   */
  public DN getEntryDN()
  {
    return compare.getEntryDN();
  }
  /**
   * {@inheritDoc}
   */
  public String getRawAttributeType()
  {
    return compare.getRawAttributeType();
  }
  /**
   * {@inheritDoc}
   */
  public void setRawAttributeType(String rawAttributeType)
  {
    compare.setRawAttributeType(rawAttributeType);
  }
  /**
   * {@inheritDoc}
   */
  public AttributeType getAttributeType()
  {
    return compare.getAttributeType();
  }
  /**
   * {@inheritDoc}
   */
  public void setAttributeType(AttributeType attributeType)
  {
    compare.setAttributeType(attributeType);
  }
  /**
   * {@inheritDoc}
   */
  public ByteString getAssertionValue()
  {
    return compare.getAssertionValue();
  }
  /**
   * {@inheritDoc}
   */
  public void setAssertionValue(ByteString assertionValue)
  {
    compare.setAssertionValue(assertionValue);
  }
  /**
   * {@inheritDoc}
   */
  public DN getProxiedAuthorizationDN()
  {
    return compare.getProxiedAuthorizationDN();
  }
  /**
   * {@inheritDoc}
   */
  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
  {
    compare.setProxiedAuthorizationDN(proxiedAuthorizationDN);
  }
}
opendj-sdk/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
@@ -116,7 +116,7 @@
   * {@inheritDoc}
   */
  @Override
  public boolean isAllowed(CompareOperation compareOperation)
  public boolean isAllowed(LocalBackendCompareOperation compareOperation)
  {
    return true;
  }
opendj-sdk/opends/src/server/org/opends/server/core/OperationWrapper.java
New file
@@ -0,0 +1,511 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
import java.util.List;
import java.util.Map;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.CancelRequest;
import org.opends.server.types.CancelResult;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.Entry;
import org.opends.server.types.Operation;
import org.opends.server.types.OperationType;
import org.opends.server.types.ResultCode;
/**
 * This abstract class is a generic operation wrapper intended to be
 * subclassed by a specific operation wrapper.
 */
public class OperationWrapper implements Operation
{
  // The wrapped operation.
  private Operation operation;
  /**
   * Creates a new generic operation wrapper.
   *
   * @param operation  the generic operation to wrap
   */
  public OperationWrapper(Operation operation)
  {
    this.operation = operation;
  }
  /**
   * {@inheritDoc}
   */
  public void addRequestControl(Control control)
  {
    operation.addRequestControl(control);
  }
  /**
   * {@inheritDoc}
   */
  public void addResponseControl(Control control)
  {
    operation.addResponseControl(control);
  }
  /**
   * {@inheritDoc}
   */
  public void appendAdditionalLogMessage(String message)
  {
    operation.appendAdditionalLogMessage(message);
  }
  /**
   * {@inheritDoc}
   */
  public void appendErrorMessage(String message)
  {
    operation.appendErrorMessage(message);
  }
  /**
   * {@inheritDoc}
   */
  public CancelResult cancel(CancelRequest cancelRequest)
  {
    return operation.cancel(cancelRequest);
  }
  /**
   * {@inheritDoc}
   */
  public void disconnectClient(
    DisconnectReason disconnectReason,
    boolean sendNotification,
    String message,
    int messageID)
  {
    operation.disconnectClient(
      disconnectReason, sendNotification, message, messageID);
  }
  /**
   * {@inheritDoc}
   */
  public boolean dontSynchronize()
  {
    return operation.dontSynchronize();
  }
  /**
   * {@inheritDoc}
   */
  public StringBuilder getAdditionalLogMessage()
  {
    return operation.getAdditionalLogMessage();
  }
  /**
   * {@inheritDoc}
   */
  public Object getAttachment(String name)
  {
    return operation.getAttachment(name);
  }
  /**
   * {@inheritDoc}
   */
  public Map<String, Object> getAttachments()
  {
    return operation.getAttachments();
  }
  /**
   * {@inheritDoc}
   */
  public DN getAuthorizationDN()
  {
    return operation.getAuthorizationDN();
  }
  /**
   * {@inheritDoc}
   */
  public Entry getAuthorizationEntry()
  {
    return operation.getAuthorizationEntry();
  }
  /**
   * {@inheritDoc}
   */
  public CancelRequest getCancelRequest()
  {
    return operation.getCancelRequest();
  }
  /**
   * {@inheritDoc}
   */
  public CancelResult getCancelResult()
  {
    return operation.getCancelResult();
  }
  /**
   * {@inheritDoc}
   */
  public ClientConnection getClientConnection()
  {
    return operation.getClientConnection();
  }
  /**
   * {@inheritDoc}
   */
  public String[][] getCommonLogElements()
  {
    return operation.getCommonLogElements();
  }
  /**
   * {@inheritDoc}
   */
  public long getConnectionID()
  {
    return operation.getConnectionID();
  }
  /**
   * {@inheritDoc}
   */
  public StringBuilder getErrorMessage()
  {
    return operation.getErrorMessage();
  }
  /**
   * {@inheritDoc}
   */
  public DN getMatchedDN()
  {
    return operation.getMatchedDN();
  }
  /**
   * {@inheritDoc}
   */
  public int getMessageID()
  {
    return operation.getMessageID();
  }
  /**
   * {@inheritDoc}
   */
  public long getOperationID()
  {
    return operation.getOperationID();
  }
  /**
   * {@inheritDoc}
   */
  public OperationType getOperationType()
  {
    return operation.getOperationType();
  }
  /**
   * {@inheritDoc}
   */
  public long getProcessingStartTime()
  {
    return operation.getProcessingStartTime();
  }
  /**
   * {@inheritDoc}
   */
  public long getProcessingStopTime()
  {
    return operation.getProcessingStopTime();
  }
  /**
   * {@inheritDoc}
   */
  public long getProcessingTime()
  {
    return operation.getProcessingTime();
  }
  /**
   * {@inheritDoc}
   */
  public List<String> getReferralURLs()
  {
    return operation.getReferralURLs();
  }
  /**
   * {@inheritDoc}
   */
  public List<Control> getRequestControls()
  {
    return operation.getRequestControls();
  }
  /**
   * {@inheritDoc}
   */
  public String[][] getRequestLogElements()
  {
    return operation.getRequestLogElements();
  }
  /**
   * {@inheritDoc}
   */
  public List<Control> getResponseControls()
  {
    return operation.getResponseControls();
  }
  /**
   * {@inheritDoc}
   */
  public String[][] getResponseLogElements()
  {
    return operation.getResponseLogElements();
  }
  /**
   * {@inheritDoc}
   */
  public ResultCode getResultCode()
  {
    return operation.getResultCode();
  }
  /**
   * {@inheritDoc}
   */
  public void indicateCancelled(CancelRequest cancelRequest)
  {
    operation.indicateCancelled(cancelRequest);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isInternalOperation()
  {
    return operation.isInternalOperation();
  }
  /**
   * {@inheritDoc}
   */
  public boolean isSynchronizationOperation()
  {
    return operation.isSynchronizationOperation();
  }
  /**
   * {@inheritDoc}
   */
  public void operationCompleted()
  {
    operation.operationCompleted();
  }
  /**
   * {@inheritDoc}
   */
  public Object removeAttachment(String name)
  {
    return operation.removeAttachment(name);
  }
  /**
   * {@inheritDoc}
   */
  public void removeRequestControl(Control control)
  {
    operation.removeRequestControl(control);
  }
  /**
   * {@inheritDoc}
   */
  public void removeResponseControl(Control control)
  {
    operation.removeResponseControl(control);
  }
  /**
   * {@inheritDoc}
   */
  public void setAdditionalLogMessage(StringBuilder additionalLogMessage)
  {
    operation.setAdditionalLogMessage(additionalLogMessage);
  }
  /**
   * {@inheritDoc}
   */
  public Object setAttachment(String name, Object value)
  {
    return operation.setAttachment(name, value);
  }
  /**
   * {@inheritDoc}
   */
  public void setAttachments(Map<String, Object> attachments)
  {
    operation.setAttachments(attachments);
  }
  /**
   * {@inheritDoc}
   */
  public void setAuthorizationEntry(Entry authorizationEntry)
  {
    operation.setAuthorizationEntry(authorizationEntry);
  }
  /**
   * {@inheritDoc}
   */
  public boolean setCancelRequest(CancelRequest cancelRequest)
  {
    return operation.setCancelRequest(cancelRequest);
  }
  /**
   * {@inheritDoc}
   */
  public void setCancelResult(CancelResult cancelResult)
  {
    operation.setCancelResult(cancelResult);
  }
  /**
   * {@inheritDoc}
   */
  public void setDontSynchronize(boolean dontSynchronize)
  {
    operation.setDontSynchronize(dontSynchronize);
  }
  /**
   * {@inheritDoc}
   */
  public void setErrorMessage(StringBuilder errorMessage)
  {
    operation.setErrorMessage(errorMessage);
  }
  /**
   * {@inheritDoc}
   */
  public void setInternalOperation(boolean isInternalOperation)
  {
    operation.setInternalOperation(isInternalOperation);
  }
  /**
   * {@inheritDoc}
   */
  public void setMatchedDN(DN matchedDN)
  {
    operation.setMatchedDN(matchedDN);
  }
  /**
   * {@inheritDoc}
   */
  public void setProcessingStartTime()
  {
    operation.setProcessingStartTime();
  }
  /**
   * {@inheritDoc}
   */
  public void setProcessingStopTime()
  {
    operation.setProcessingStopTime();
  }
  /**
   * {@inheritDoc}
   */
  public void setReferralURLs(List<String> referralURLs)
  {
    operation.setReferralURLs(referralURLs);
  }
  /**
   * {@inheritDoc}
   */
  public void setResponseData(DirectoryException directoryException)
  {
    operation.setResponseData(directoryException);
  }
  /**
   * {@inheritDoc}
   */
  public void setResultCode(ResultCode resultCode)
  {
    operation.setResultCode(resultCode);
  }
  /**
   * {@inheritDoc}
   */
  public void setSynchronizationOperation(boolean isSynchronizationOperation)
  {
    operation.setSynchronizationOperation(isSynchronizationOperation);
  }
  /**
   * {@inheritDoc}
   */
  public void toString(StringBuilder buffer)
  {
    operation.toString(buffer);
  }
}
opendj-sdk/opends/src/server/org/opends/server/core/PluginConfigManager.java
@@ -82,21 +82,25 @@
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.operation.PostOperationAddOperation;
import org.opends.server.types.operation.PostOperationBindOperation;
import org.opends.server.types.operation.PostOperationCompareOperation;
import org.opends.server.types.operation.PostOperationDeleteOperation;
import org.opends.server.types.operation.PostOperationModifyOperation;
import org.opends.server.types.operation.PostOperationSearchOperation;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.operation.PostResponseBindOperation;
import org.opends.server.types.operation.PostResponseCompareOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import org.opends.server.types.operation.PostResponseSearchOperation;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PreOperationBindOperation;
import org.opends.server.types.operation.PreOperationCompareOperation;
import org.opends.server.types.operation.PreOperationDeleteOperation;
import org.opends.server.types.operation.PreOperationModifyOperation;
import org.opends.server.types.operation.PreOperationSearchOperation;
import org.opends.server.types.operation.PreParseAddOperation;
import org.opends.server.types.operation.PreParseBindOperation;
import org.opends.server.types.operation.PreParseCompareOperation;
import org.opends.server.types.operation.PreParseDeleteOperation;
import org.opends.server.types.operation.PreParseModifyOperation;
import org.opends.server.types.operation.PreParseSearchOperation;
@@ -1971,7 +1975,7 @@
   * @return  The result of processing the pre-parse compare plugins.
   */
  public PreParsePluginResult invokePreParseComparePlugins(
                                   CompareOperation compareOperation)
    PreParseCompareOperation compareOperation)
  {
    PreParsePluginResult result = null;
@@ -2705,7 +2709,7 @@
   * @return  The result of processing the pre-operation compare plugins.
   */
  public PreOperationPluginResult invokePreOperationComparePlugins(
                                       CompareOperation compareOperation)
     PreOperationCompareOperation compareOperation)
  {
    PreOperationPluginResult result = null;
@@ -3439,7 +3443,7 @@
   * @return  The result of processing the post-operation compare plugins.
   */
  public PostOperationPluginResult invokePostOperationComparePlugins(
                                        CompareOperation compareOperation)
      PostOperationCompareOperation compareOperation)
  {
    PostOperationPluginResult result = null;
@@ -4161,7 +4165,7 @@
   * @return  The result of processing the post-response compare plugins.
   */
  public PostResponsePluginResult invokePostResponseComparePlugins(
                                       CompareOperation compareOperation)
      PostResponseCompareOperation compareOperation)
  {
    PostResponsePluginResult result = null;
opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -954,8 +954,8 @@
                                         String attributeType,
                                         ByteString assertionValue)
  {
    CompareOperation compareOperation =
         new CompareOperation(this, nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(this, nextOperationID(),
                              nextMessageID(),
                              new ArrayList<Control>(0), rawEntryDN,
                              attributeType, assertionValue);
@@ -985,8 +985,8 @@
                                         AttributeType attributeType,
                                         ByteString assertionValue)
  {
    CompareOperation compareOperation =
         new CompareOperation(this, nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(this, nextOperationID(),
                              nextMessageID(),
                              new ArrayList<Control>(0), entryDN,
                              attributeType, assertionValue);
opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
@@ -589,8 +589,8 @@
                                        String attributeType,
                                        ASN1OctetString assertionValue)
  {
    CompareOperation compareOperation =
         new CompareOperation(this, nextOperationID(), nextMessageID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(this, nextOperationID(), nextMessageID(),
                              new ArrayList<Control>(0), rawEntryDN,
                              attributeType, assertionValue);
opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -56,7 +56,7 @@
import org.opends.server.core.AbandonOperation;
import org.opends.server.core.AddOperationBasis;
import org.opends.server.core.BindOperationBasis;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.CompareOperationBasis;
import org.opends.server.core.DeleteOperationBasis;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
@@ -2063,8 +2063,8 @@
    }
    CompareRequestProtocolOp protocolOp = message.getCompareRequestProtocolOp();
    CompareOperation compareOp =
         new CompareOperation(this, nextOperationID.getAndIncrement(),
    CompareOperationBasis compareOp =
         new CompareOperationBasis(this, nextOperationID.getAndIncrement(),
                              message.getMessageID(), controls,
                              protocolOp.getDN(), protocolOp.getAttributeType(),
                              protocolOp.getAssertionValue());
opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
New file
@@ -0,0 +1,86 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.workflowelement.localbackend;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.CompareOperationWrapper;
import org.opends.server.types.Entry;
import org.opends.server.types.operation.PostOperationCompareOperation;
import org.opends.server.types.operation.PostResponseCompareOperation;
import org.opends.server.types.operation.PreOperationCompareOperation;
/**
 * This class defines an operation that may be used to determine whether a
 * specified entry in the Directory Server contains a given attribute-value
 * pair.
 */
public class LocalBackendCompareOperation extends CompareOperationWrapper
    implements PreOperationCompareOperation,
               PostOperationCompareOperation,
               PostResponseCompareOperation
{
  // The entry to be compared.
  private Entry entry = null;
  /**
   * Creates a new compare operation based on the provided compare operation.
   *
   * @param compare  the compare operation
   */
  public LocalBackendCompareOperation(CompareOperation compare)
  {
    super(compare);
    LocalBackendWorkflowElement.attachLocalOperation (compare, this);
  }
  /**
   * Retrieves the entry to target with the compare operation.
   *
   * @return  The entry to target with the compare operation, or
   *          <CODE>null</CODE> if the entry is not yet available.
   */
  public Entry getEntryToCompare()
  {
    return entry;
  }
  /**
   * Set the entry to target with the compare operation.
   *
   * @param  entry   The entry to target with the compare operation.
   */
  public void setEntryToCompare(Entry entry)
  {
    this.entry = entry;
  }
}
opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -74,6 +74,7 @@
import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.AddOperation;
import org.opends.server.core.BindOperation;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
@@ -178,6 +179,9 @@
    case MODIFY:
      processModify((ModifyOperation) operation);
      break;
    case COMPARE:
      processCompare((CompareOperation) operation);
      break;
    default:
      // jdemendi - temporary code, just make sure that we don't fell into
      // that incomplete code...
@@ -5860,9 +5864,9 @@
  /**
   * Perform a delete operation against a local backend.
   * Performs a delete operation against a local backend.
   *
   * @param operation - The operation to perform
   * @param operation the operation to perform
   */
  public void processDelete(DeleteOperation operation){
    LocalBackendDeleteOperation localOperation =
@@ -5871,9 +5875,9 @@
  }
  /**
   * Perform a local delete operation against a local backend.
   * Performs a local delete operation against a local backend.
   *
   * @param operation - The operation to perform
   * @param operation the operation to perform
   */
  private void processLocalDelete(LocalBackendDeleteOperation localOp)
  {
@@ -6673,6 +6677,576 @@
  /**
   * Perform a compare operation against a local backend.
   *
   * @param operation - The operation to perform
   */
  public void processCompare(CompareOperation operation)
  {
    LocalBackendCompareOperation localOperation =
      new LocalBackendCompareOperation(operation);
    processLocalCompare(localOperation);
  }
  /**
   * Perform a local compare operation against a local backend.
   *
   * @param operation - The operation to perform
   */
  private void processLocalCompare(LocalBackendCompareOperation localOp)
  {
    // Get the plugin config manager that will be used for invoking plugins.
    PluginConfigManager pluginConfigManager =
         DirectoryServer.getPluginConfigManager();
    boolean skipPostOperation = false;
    // Get a reference to the client connection
    ClientConnection clientConnection = localOp.getClientConnection();
    // Check for and handle a request to cancel this operation.
    if (localOp.getCancelRequest() != null)
    {
      return;
    }
    // Create a labeled block of code that we can break out of if a problem is
    // detected.
compareProcessing:
    {
      // Process the entry DN to convert it from the raw form to the form
      // required for the rest of the compare processing.
      DN entryDN = localOp.getEntryDN();
      if (entryDN == null)
      {
        skipPostOperation = true;
        break compareProcessing;
      }
      // If the target entry is in the server configuration, then make sure the
      // requester has the CONFIG_READ privilege.
      if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) &&
          (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, localOp)))
      {
        int msgID = MSGID_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES;
        localOp.appendErrorMessage(getMessage(msgID));
        localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
        skipPostOperation = true;
        break compareProcessing;
      }
      // Check for and handle a request to cancel this operation.
      if (localOp.getCancelRequest() != null)
      {
        return;
      }
      // Grab a read lock on the entry.
      Lock readLock = null;
      for (int i=0; i < 3; i++)
      {
        readLock = LockManager.lockRead(entryDN);
        if (readLock != null)
        {
          break;
        }
      }
      if (readLock == null)
      {
        int    msgID   = MSGID_COMPARE_CANNOT_LOCK_ENTRY;
        String message = getMessage(msgID, String.valueOf(entryDN));
        localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
        localOp.appendErrorMessage(message);
        skipPostOperation = true;
        break compareProcessing;
      }
      Entry entry = null;
      try
      {
        // Get the entry.  If it does not exist, then fail.
        try
        {
          entry = DirectoryServer.getEntry(entryDN);
          localOp.setEntryToCompare(entry);
          if (entry == null)
          {
            localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
            localOp.appendErrorMessage(getMessage(MSGID_COMPARE_NO_SUCH_ENTRY,
                                          String.valueOf(entryDN)));
            // See if one of the entry's ancestors exists.
            DN parentDN = entryDN.getParentDNInSuffix();
            while (parentDN != null)
            {
              try
              {
                if (DirectoryServer.entryExists(parentDN))
                {
                  localOp.setMatchedDN(parentDN);
                  break;
                }
              }
              catch (Exception e)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
                }
                break;
              }
              parentDN = parentDN.getParentDNInSuffix();
            }
            break compareProcessing;
          }
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, de);
          }
          localOp.setResultCode(de.getResultCode());
          localOp.appendErrorMessage(de.getErrorMessage());
          break compareProcessing;
        }
        // Check to see if there are any controls in the request.  If so, then
        // see if there is any special processing required.
        List<Control> requestControls = localOp.getRequestControls();
        if ((requestControls != null) && (! requestControls.isEmpty()))
        {
          for (int i=0; i < requestControls.size(); i++)
          {
            Control c   = requestControls.get(i);
            String  oid = c.getOID();
            if (oid.equals(OID_LDAP_ASSERTION))
            {
              LDAPAssertionRequestControl assertControl;
              if (c instanceof LDAPAssertionRequestControl)
              {
                assertControl = (LDAPAssertionRequestControl) c;
              }
              else
              {
                try
                {
                  assertControl = LDAPAssertionRequestControl.decodeControl(c);
                  requestControls.set(i, assertControl);
                }
                catch (LDAPException le)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
                  }
                  localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
                  localOp.appendErrorMessage(le.getMessage());
                  break compareProcessing;
                }
              }
              try
              {
                // FIXME -- We need to determine whether the current user has
                //          permission to make this determination.
                SearchFilter filter = assertControl.getSearchFilter();
                if (! filter.matchesEntry(entry))
                {
                  localOp.setResultCode(ResultCode.ASSERTION_FAILED);
                  localOp.appendErrorMessage(
                      getMessage(MSGID_COMPARE_ASSERTION_FAILED,
                      String.valueOf(entryDN)));
                  break compareProcessing;
                }
              }
              catch (DirectoryException de)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
                }
                localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
                int msgID = MSGID_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER;
                localOp.appendErrorMessage(
                    getMessage(msgID, String.valueOf(entryDN),
                    de.getErrorMessage()));
                break compareProcessing;
              }
            }
            else if (oid.equals(OID_PROXIED_AUTH_V1))
            {
              // The requester must have the PROXIED_AUTH privilige in order to
              // be able to use this control.
              if (! clientConnection.hasPrivilege(
                       Privilege.PROXIED_AUTH, localOp))
              {
                int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
                localOp.appendErrorMessage(getMessage(msgID));
                localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
                break compareProcessing;
              }
              ProxiedAuthV1Control proxyControl;
              if (c instanceof ProxiedAuthV1Control)
              {
                proxyControl = (ProxiedAuthV1Control) c;
              }
              else
              {
                try
                {
                  proxyControl = ProxiedAuthV1Control.decodeControl(c);
                }
                catch (LDAPException le)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
                  }
                  localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
                  localOp.appendErrorMessage(le.getMessage());
                  break compareProcessing;
                }
              }
              Entry authorizationEntry;
              try
              {
                authorizationEntry = proxyControl.getAuthorizationEntry();
              }
              catch (DirectoryException de)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
                }
                localOp.setResultCode(de.getResultCode());
                localOp.appendErrorMessage(de.getErrorMessage());
                break compareProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(localOp,
                                                 authorizationEntry) == false) {
                localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                localOp.appendErrorMessage(
                    getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break compareProcessing;
              }
              localOp.setAuthorizationEntry(authorizationEntry);
              if (authorizationEntry == null)
              {
                localOp.setProxiedAuthorizationDN(DN.nullDN());
              }
              else
              {
                localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
              }
            }
            else if (oid.equals(OID_PROXIED_AUTH_V2))
            {
              // The requester must have the PROXIED_AUTH privilige in order to
              // be able to use this control.
              if (! clientConnection.hasPrivilege(
                       Privilege.PROXIED_AUTH, localOp))
              {
                int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
                localOp.appendErrorMessage(getMessage(msgID));
                localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
                break compareProcessing;
              }
              ProxiedAuthV2Control proxyControl;
              if (c instanceof ProxiedAuthV2Control)
              {
                proxyControl = (ProxiedAuthV2Control) c;
              }
              else
              {
                try
                {
                  proxyControl = ProxiedAuthV2Control.decodeControl(c);
                }
                catch (LDAPException le)
                {
                  if (debugEnabled())
                  {
                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
                  }
                  localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
                  localOp.appendErrorMessage(le.getMessage());
                  break compareProcessing;
                }
              }
              Entry authorizationEntry;
              try
              {
                authorizationEntry = proxyControl.getAuthorizationEntry();
              }
              catch (DirectoryException de)
              {
                if (debugEnabled())
                {
                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
                }
                localOp.setResultCode(de.getResultCode());
                localOp.appendErrorMessage(de.getErrorMessage());
                break compareProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(localOp,
                                                 authorizationEntry) == false) {
                localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                localOp.appendErrorMessage(
                    getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break compareProcessing;
              }
              localOp.setAuthorizationEntry(authorizationEntry);
              if (authorizationEntry == null)
              {
                localOp.setProxiedAuthorizationDN(DN.nullDN());
              }
              else
              {
                localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
              }
            }
            // NYI -- Add support for additional controls.
            else if (c.isCritical())
            {
              Backend backend = DirectoryServer.getBackend(entryDN);
              if ((backend == null) || (! backend.supportsControl(oid)))
              {
                localOp.setResultCode(
                    ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
                int msgID = MSGID_COMPARE_UNSUPPORTED_CRITICAL_CONTROL;
                localOp.appendErrorMessage(
                    getMessage(msgID, String.valueOf(entryDN), oid));
                break compareProcessing;
              }
            }
          }
        }
        // Check to see if the client has permission to perform the
        // compare.
        // FIXME: for now assume that this will check all permission
        // pertinent to the operation. This includes proxy authorization
        // and any other controls specified.
        // FIXME: earlier checks to see if the entry already exists may
        // have already exposed sensitive information to the client.
        if (AccessControlConfigManager.getInstance()
            .getAccessControlHandler().isAllowed(localOp) == false) {
          localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
          int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
          localOp.appendErrorMessage(
              getMessage(msgID, String.valueOf(entryDN)));
          skipPostOperation = true;
          break compareProcessing;
        }
        // Check for and handle a request to cancel this operation.
        if (localOp.getCancelRequest() != null)
        {
          return;
        }
        // Invoke the pre-operation compare plugins.
        PreOperationPluginResult preOpResult =
             pluginConfigManager.invokePreOperationComparePlugins(localOp);
        if (preOpResult.connectionTerminated())
        {
          // There's no point in continuing with anything.  Log the request and
          // result and return.
          localOp.setResultCode(ResultCode.CANCELED);
          int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT;
          localOp.appendErrorMessage(getMessage(msgID));
          return;
        }
        else if (preOpResult.sendResponseImmediately())
        {
          skipPostOperation = true;
          break compareProcessing;
        }
        else if (preOpResult.skipCoreProcessing())
        {
          skipPostOperation = false;
          break compareProcessing;
        }
        // Get the base attribute type and set of options.
        String          baseName;
        HashSet<String> options;
        String rawAttributeType = localOp.getRawAttributeType();
        int             semicolonPos = rawAttributeType.indexOf(';');
        if (semicolonPos > 0)
        {
          baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos));
          options = new HashSet<String>();
          int nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
          while (nextPos > 0)
          {
            options.add(rawAttributeType.substring(semicolonPos+1, nextPos));
            semicolonPos = nextPos;
            nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
          }
          options.add(rawAttributeType.substring(semicolonPos+1));
        }
        else
        {
          baseName = toLowerCase(rawAttributeType);
          options  = null;
        }
        // Actually perform the compare operation.
        List<Attribute> attrList = null;
        if (localOp.getAttributeType() == null)
        {
          localOp.setAttributeType(DirectoryServer.getAttributeType(baseName));
        }
        if (localOp.getAttributeType() == null)
        {
          attrList = entry.getAttribute(baseName, options);
          localOp.setAttributeType(
              DirectoryServer.getDefaultAttributeType(baseName));
        }
        else
        {
          attrList = entry.getAttribute(localOp.getAttributeType(), options);
        }
        if ((attrList == null) || attrList.isEmpty())
        {
          localOp.setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
          if (options == null)
          {
            localOp.appendErrorMessage(getMessage(MSGID_COMPARE_OP_NO_SUCH_ATTR,
                                          String.valueOf(entryDN), baseName));
          }
          else
          {
            localOp.appendErrorMessage(getMessage(
                                    MSGID_COMPARE_OP_NO_SUCH_ATTR_WITH_OPTIONS,
                                    String.valueOf(entryDN), baseName));
          }
        }
        else
        {
          AttributeValue value = new AttributeValue(
              localOp.getAttributeType(),
              localOp.getAssertionValue());
          boolean matchFound = false;
          for (Attribute a : attrList)
          {
            if (a.hasValue(value))
            {
              matchFound = true;
              break;
            }
          }
          if (matchFound)
          {
            localOp.setResultCode(ResultCode.COMPARE_TRUE);
          }
          else
          {
            localOp.setResultCode(ResultCode.COMPARE_FALSE);
          }
        }
      }
      finally
      {
        LockManager.unlock(entryDN, readLock);
      }
    }
    // Check for and handle a request to cancel this operation.
    if (localOp.getCancelRequest() != null)
    {
      return;
    }
    // Invoke the post-operation compare plugins.
    if (! skipPostOperation)
    {
      PostOperationPluginResult postOperationResult =
           pluginConfigManager.invokePostOperationComparePlugins(localOp);
      if (postOperationResult.connectionTerminated())
      {
        localOp.setResultCode(ResultCode.CANCELED);
        int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT;
        localOp.appendErrorMessage(getMessage(msgID));
        return;
      }
    }
  }
  /**
   * Attaches the current local operation to the global operation so that
   * operation runner can execute local operation post response later on.
   *
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/CompareOperationTestCase.java
@@ -48,7 +48,6 @@
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
@@ -138,7 +137,8 @@
    return new Operation[]
    {
      new CompareOperation(conn, InternalClientConnection.nextOperationID(),
      new CompareOperationBasis(
                           conn, InternalClientConnection.nextOperationID(),
                           InternalClientConnection.nextMessageID(),
                           new ArrayList<Control>(),
                           new ASN1OctetString(entry.getDN().toString()),
@@ -216,8 +216,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -240,8 +241,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -263,8 +265,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString("o=nonexistent,o=test"),
@@ -285,8 +288,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString("rogasawara,o=test"),
@@ -307,8 +311,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -330,8 +335,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -352,8 +358,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -371,8 +378,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -390,8 +398,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -409,8 +418,9 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              new ArrayList<Control>(),
                              new ASN1OctetString(entry.getDN().toString()),
@@ -436,8 +446,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(assertControl);
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
                              new ASN1OctetString(entry.getDN().toString()),
@@ -465,8 +476,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(assertControl);
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
                              new ASN1OctetString(entry.getDN().toString()),
@@ -490,8 +502,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(authV1Control);
    CompareOperation compareOperation =
         new CompareOperation(proxyUserConn,
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              proxyUserConn,
                              InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
@@ -518,8 +531,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(authV1Control);
    CompareOperation compareOperation =
         new CompareOperation(proxyUserConn,
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              proxyUserConn,
                              InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
@@ -544,8 +558,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(authV2Control);
    CompareOperation compareOperation =
         new CompareOperation(proxyUserConn,
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              proxyUserConn,
                              InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
@@ -570,8 +585,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(authV2Control);
    CompareOperation compareOperation =
         new CompareOperation(proxyUserConn,
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              proxyUserConn,
                              InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
@@ -597,8 +613,9 @@
    List<Control> controls = new ArrayList<Control>();
    controls.add(authV2Control);
    CompareOperation compareOperation =
         new CompareOperation(proxyUserConn,
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              proxyUserConn,
                              InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
@@ -625,8 +642,9 @@
         new LDAPAssertionRequestControl("1.1.1.1.1.1", true, ldapFilter);
    List<Control> controls = new ArrayList<Control>();
    controls.add(assertControl);
    CompareOperation compareOperation =
         new CompareOperation(conn, InternalClientConnection.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(
                              conn, InternalClientConnection.nextOperationID(),
                              InternalClientConnection.nextMessageID(),
                              controls,
                              new ASN1OctetString(entry.getDN().toString()),
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxPrivilegeTestCase.java
@@ -44,7 +44,6 @@
import java.util.UUID;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.ClassLoaderProvider;
import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskBackend;
import org.opends.server.backends.task.TaskState;
@@ -53,6 +52,7 @@
import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationBasis;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.CompareOperationBasis;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DeleteOperationBasis;
import org.opends.server.core.DirectoryServer;
@@ -1260,8 +1260,8 @@
    // Test a compare operation against the PWReset Target user.
    CompareOperation compareOperation =
         new CompareOperation(conn, conn.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(conn, conn.nextOperationID(),
                              conn.nextMessageID(), controls, targetDN,
                              DirectoryServer.getAttributeType("cn", true),
                              ByteStringFactory.create("PWReset Target"));
@@ -1467,8 +1467,8 @@
    // Test a compare operation against the PWReset Target user.
    CompareOperation compareOperation =
         new CompareOperation(conn, conn.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(conn, conn.nextOperationID(),
                              conn.nextMessageID(), controls, targetDN,
                              DirectoryServer.getAttributeType("cn", true),
                              ByteStringFactory.create("PWReset Target"));
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
@@ -48,6 +48,7 @@
import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationBasis;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.CompareOperationBasis;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DeleteOperationBasis;
import org.opends.server.core.DirectoryServer;
@@ -1319,8 +1320,8 @@
    // Test a compare operation against the PWReset Target user.
    CompareOperation compareOperation =
         new CompareOperation(conn, conn.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(conn, conn.nextOperationID(),
                              conn.nextMessageID(), controls, targetDN,
                              DirectoryServer.getAttributeType("cn", true),
                              ByteStringFactory.create("PWReset Target"));
@@ -1524,8 +1525,8 @@
    // Test a compare operation against the PWReset Target user.
    CompareOperation compareOperation =
         new CompareOperation(conn, conn.nextOperationID(),
    CompareOperationBasis compareOperation =
         new CompareOperationBasis(conn, conn.nextOperationID(),
                              conn.nextMessageID(), controls, targetDN,
                              DirectoryServer.getAttributeType("cn", true),
                              ByteStringFactory.create("PWReset Target"));