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

neil_a_wilson
25.54.2007 459b0d746ffd89bdf8fcd47fa3a14e76be87136f
Further split up the local backend modify operation to allow it to be
better optimized, and do the same for SearchFilter.matchesEntryInternal and
Entry.conformsToSchema.

Also, fix a problem in the local backend operations where cancel requests
were not being handled correctly.
8 files modified
2237 ■■■■■ changed files
opends/src/server/org/opends/server/types/Entry.java 286 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/SearchFilter.java 1574 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java 31 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java 29 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java 27 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java 31 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java 232 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java 27 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Entry.java
@@ -2438,6 +2438,70 @@
    }
    if (! checkAttributesAndObjectClasses(ditContentRule,
               structuralPolicy, invalidReason))
    {
      return false;
    }
    // If there is a name form for this entry, then make sure that the
    // RDN for the entry is in compliance with it.
    if (nameForm != null)
    {
      if (! checkNameForm(nameForm, structuralPolicy, invalidReason))
      {
        return false;
      }
    }
    // If there is a DIT content rule for this entry, then make sure
    // that the entry is in compliance with it.
    if (ditContentRule != null)
    {
      if (! checkDITContentRule(ditContentRule, structuralPolicy,
                                invalidReason))
      {
        return false;
      }
    }
    if (! checkDITStructureRule(ditStructureRule, structuralClass,
               parentEntry, parentProvided, validateStructureRules,
               structuralPolicy, invalidReason))
    {
      return false;
    }
    // If we've gotten here, then the entry is acceptable.
    return true;
  }
  /**
   * Checks the attributes and object classes contained in this entry
   * to determine whether they conform to the server schema
   * requirements.
   *
   * @param  ditContentRule    The DIT content rule for this entry, if
   *                           any.
   * @param  structuralPolicy  The policy that should be used for
   *                           structural object class compliance.
   * @param  invalidReason     A buffer into which an invalid reason
   *                           may be added.
   *
   * @return {@code true} if this entry passes all of the checks, or
   *         {@code false} if there are any failures.
   */
  private boolean checkAttributesAndObjectClasses(
                       DITContentRule ditContentRule,
                       AcceptRejectWarn structuralPolicy,
                       MessageBuilder invalidReason)
  {
    // Make sure that we recognize all of the objectclasses, that all
    // auxiliary classes are allowed by the DIT content rule, and that
    // all attributes required by the object classes are present.
@@ -2579,80 +2643,42 @@
    }
    // If there is a name form for this entry, then make sure that the
    // RDN for the entry is in compliance with it.
    if (nameForm != null)
    // If we've gotten here, then things are OK.
    return true;
  }
  /**
   * Performs any processing needed for name form validation.
   *
   * @param  nameForm          The name form to validate against this
   *                           entry.
   * @param  structuralPolicy  The policy that should be used for
   *                           structural object class compliance.
   * @param  invalidReason     A buffer into which an invalid reason
   *                           may be added.
   *
   * @return {@code true} if this entry passes all of the checks, or
   *         {@code false} if there are any failures.
   */
  private boolean checkNameForm(NameForm nameForm,
                       AcceptRejectWarn structuralPolicy,
                       MessageBuilder invalidReason)
  {
    RDN rdn = dn.getRDN();
    if (rdn != null)
    {
      RDN rdn = dn.getRDN();
      if (rdn != null)
      // Make sure that all the required attributes are present.
      for (AttributeType t : nameForm.getRequiredAttributes())
      {
        // Make sure that all the required attributes are present.
        for (AttributeType t : nameForm.getRequiredAttributes())
        {
          if (! rdn.hasAttributeType(t))
          {
            Message message =
                    ERR_ENTRY_SCHEMA_RDN_MISSING_REQUIRED_ATTR.get(
                      String.valueOf(dn),
                      t.getNameOrOID(),
                      nameForm.getNameOrOID());
            if (structuralPolicy == AcceptRejectWarn.REJECT)
            {
              invalidReason.append(message);
              return false;
            }
            else if (structuralPolicy == AcceptRejectWarn.WARN)
            {
              logError(message);
            }
          }
        }
        // Make sure that all attributes in the RDN are allowed.
        int numAVAs = rdn.getNumValues();
        for (int i = 0; i < numAVAs; i++)
        {
          AttributeType t = rdn.getAttributeType(i);
          if (! nameForm.isRequiredOrOptional(t))
          {
            Message message =
                    ERR_ENTRY_SCHEMA_RDN_DISALLOWED_ATTR.get(
                      String.valueOf(dn),
                      t.getNameOrOID(),
                      nameForm.getNameOrOID());
            if (structuralPolicy == AcceptRejectWarn.REJECT)
            {
              invalidReason.append(message);
              return false;
            }
            else if (structuralPolicy == AcceptRejectWarn.WARN)
            {
              logError(message);
            }
          }
        }
      }
    }
    // If there is a DIT content rule for this entry, then make sure
    // that the entry is in compliance with it.
    if (ditContentRule != null)
    {
      // Make sure that all of the required attributes are present.
      for (AttributeType t : ditContentRule.getRequiredAttributes())
      {
        if (! (userAttributes.containsKey(t) ||
               operationalAttributes.containsKey(t) ||
               t.isObjectClassType()))
        if (! rdn.hasAttributeType(t))
        {
          Message message =
                  ERR_ENTRY_SCHEMA_MISSING_REQUIRED_ATTR_FOR_DCR.get(
                  ERR_ENTRY_SCHEMA_RDN_MISSING_REQUIRED_ATTR.get(
                    String.valueOf(dn),
                    t.getNameOrOID(),
                    ditContentRule.getName());
                    nameForm.getNameOrOID());
          if (structuralPolicy == AcceptRejectWarn.REJECT)
          {
@@ -2666,17 +2692,18 @@
        }
      }
      // Make sure that none of the prohibited attributes are present.
      for (AttributeType t : ditContentRule.getProhibitedAttributes())
      // Make sure that all attributes in the RDN are allowed.
      int numAVAs = rdn.getNumValues();
      for (int i = 0; i < numAVAs; i++)
      {
        if (userAttributes.containsKey(t) ||
            operationalAttributes.containsKey(t))
        AttributeType t = rdn.getAttributeType(i);
        if (! nameForm.isRequiredOrOptional(t))
        {
          Message message =
                  ERR_ENTRY_SCHEMA_PROHIBITED_ATTR_FOR_DCR.get(
                  ERR_ENTRY_SCHEMA_RDN_DISALLOWED_ATTR.get(
                    String.valueOf(dn),
                    t.getNameOrOID(),
                    ditContentRule.getName());
                    nameForm.getNameOrOID());
          if (structuralPolicy == AcceptRejectWarn.REJECT)
          {
@@ -2691,7 +2718,115 @@
      }
    }
    // If we've gotten here, then things are OK.
    return true;
  }
  /**
   * Performs any processing needed for DIT content rule validation.
   *
   * @param  ditContentRule    The DIT content rule to validate
   *                           against this entry.
   * @param  structuralPolicy  The policy that should be used for
   *                           structural object class compliance.
   * @param  invalidReason     A buffer into which an invalid reason
   *                           may be added.
   *
   * @return {@code true} if this entry passes all of the checks, or
   *         {@code false} if there are any failures.
   */
  private boolean checkDITContentRule(DITContentRule ditContentRule,
                       AcceptRejectWarn structuralPolicy,
                       MessageBuilder invalidReason)
  {
    // Make sure that all of the required attributes are present.
    for (AttributeType t : ditContentRule.getRequiredAttributes())
    {
      if (! (userAttributes.containsKey(t) ||
             operationalAttributes.containsKey(t) ||
             t.isObjectClassType()))
      {
        Message message =
                ERR_ENTRY_SCHEMA_MISSING_REQUIRED_ATTR_FOR_DCR.get(
                  String.valueOf(dn),
                  t.getNameOrOID(),
                  ditContentRule.getName());
        if (structuralPolicy == AcceptRejectWarn.REJECT)
        {
          invalidReason.append(message);
          return false;
        }
        else if (structuralPolicy == AcceptRejectWarn.WARN)
        {
          logError(message);
        }
      }
    }
    // Make sure that none of the prohibited attributes are present.
    for (AttributeType t : ditContentRule.getProhibitedAttributes())
    {
      if (userAttributes.containsKey(t) ||
          operationalAttributes.containsKey(t))
      {
        Message message =
                ERR_ENTRY_SCHEMA_PROHIBITED_ATTR_FOR_DCR.get(
                  String.valueOf(dn),
                  t.getNameOrOID(),
                  ditContentRule.getName());
        if (structuralPolicy == AcceptRejectWarn.REJECT)
        {
          invalidReason.append(message);
          return false;
        }
        else if (structuralPolicy == AcceptRejectWarn.WARN)
        {
          logError(message);
        }
      }
    }
    // If we've gotten here, then things are OK.
    return true;
  }
  /**
   * Performs any processing needed for DIT structure rule validation.
   *
   * @param  ditStructureRule        The DIT structure rule for this
   *                                 entry.
   * @param  structuralClass         The structural object class for
   *                                 this entry.
   * @param  parentEntry             The parent entry, if available
   *                                 and applicable.
   * @param  parentProvided          Indicates whether the parent
   *                                 entry was provided.
   * @param  validateStructureRules  Indicates whether to check to see
   *                                 if this entry violates a DIT
   *                                 structure rule for its parent.
   * @param  structuralPolicy        The policy that should be used
   *                                 for structural object class
   *                                 compliance.
   * @param  invalidReason           A buffer into which an invalid
   *                                 reason may be added.
   *
   * @return {@code true} if this entry passes all of the checks, or
   *         {@code false} if there are any failures.
   */
  private boolean checkDITStructureRule(
                       DITStructureRule ditStructureRule,
                       ObjectClass structuralClass,
                       Entry parentEntry, boolean parentProvided,
                       boolean validateStructureRules,
                       AcceptRejectWarn structuralPolicy,
                       MessageBuilder invalidReason)
  {
    // If there is a DIT structure rule for this entry, then make sure
    // that the entry is in compliance with it.
    if ((ditStructureRule != null) &&
@@ -2965,8 +3100,7 @@
      }
    }
    // If we've gotten here, then the entry is acceptable.
    // If we've gotten here, then things are OK.
    return true;
  }
opends/src/server/org/opends/server/types/SearchFilter.java
@@ -2331,691 +2331,31 @@
    switch (filterType)
    {
      case AND:
        if (filterComponents == null)
        {
          // The set of subcomponents was null.  This is not allowed.
          Message message =
              ERR_SEARCH_FILTER_COMPOUND_COMPONENTS_NULL.
                get(String.valueOf(entry.getDN()),
                    String.valueOf(completeFilter),
                    String.valueOf(filterType));
          throw new DirectoryException(
                         DirectoryServer.getServerErrorResultCode(),
                         message);
        }
        else if (filterComponents.isEmpty())
        {
          // An AND filter with no elements like "(&)" is specified as
          // "undefined" in RFC 2251, but is considered one of the
          // TRUE/FALSE filters in RFC 4526, in which case we should
          // always return true.
          if (debugEnabled())
          {
            TRACER.debugInfo("Returning TRUE for LDAP TRUE " +
                "filter (&)");
          }
          return ConditionResult.TRUE;
        }
        else
        {
          // We will have to evaluate one or more subcomponents.  In
          // this case, first check our depth to make sure we're not
          // nesting too deep.
          if (depth >= MAX_NESTED_FILTER_DEPTH)
          {
            Message message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP.
                get(String.valueOf(entry.getDN()),
                    String.valueOf(completeFilter));
            throw new DirectoryException(
                           DirectoryServer.getServerErrorResultCode(),
                           message);
          }
          for (SearchFilter f : filterComponents)
          {
            ConditionResult result =
                 f.matchesEntryInternal(completeFilter, entry,
                                     depth+1);
            switch (result)
            {
              case TRUE:
                break;
              case FALSE:
                if (debugEnabled())
                {
                  TRACER.debugVerbose(
                      "Returning FALSE for AND component %s in " +
                      "filter %s for entry %s",
                               f, completeFilter, entry.getDN());
                }
                return result;
              case UNDEFINED:
                if (debugEnabled())
                {
                  TRACER.debugInfo(
                 "Undefined result for AND component %s in filter " +
                 "%s for entry %s", f, completeFilter, entry.getDN());
                }
                return result;
              default:
                Message message =
                    ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.
                      get(String.valueOf(entry.getDN()),
                          String.valueOf(completeFilter),
                          String.valueOf(result));
                throw new
                     DirectoryException(
                          DirectoryServer.getServerErrorResultCode(),
                          message);
            }
          }
          // If we have gotten here, then all the components must have
          // matched.
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning TRUE for AND component %s in filter %s " +
                "for entry %s", this, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        }
        return processAND(completeFilter, entry, depth);
      case OR:
        if (filterComponents == null)
        {
          // The set of subcomponents was null.  This is not allowed.
          Message message =
              ERR_SEARCH_FILTER_COMPOUND_COMPONENTS_NULL.
                get(String.valueOf(entry.getDN()),
                    String.valueOf(completeFilter),
                    String.valueOf(filterType));
          throw new DirectoryException(
                         DirectoryServer.getServerErrorResultCode(),
                         message);
        }
        else if (filterComponents.isEmpty())
        {
          // An OR filter with no elements like "(|)" is specified as
          // "undefined" in RFC 2251, but is considered one of the
          // TRUE/FALSE filters in RFC 4526, in which case we should
          // always return false.
          if (debugEnabled())
          {
            TRACER.debugInfo("Returning FALSE for LDAP FALSE " +
                "filter (|)");
          }
          return ConditionResult.FALSE;
        }
        else
        {
          // We will have to evaluate one or more subcomponents.  In
          // this case, first check our depth to make sure we're not
          // nesting too deep.
          if (depth >= MAX_NESTED_FILTER_DEPTH)
          {
            Message message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP.
                get(String.valueOf(entry.getDN()),
                    String.valueOf(completeFilter));
            throw new DirectoryException(
                           DirectoryServer.getServerErrorResultCode(),
                           message);
          }
          ConditionResult result = ConditionResult.FALSE;
          for (SearchFilter f : filterComponents)
          {
            switch (f.matchesEntryInternal(completeFilter, entry,
                                   depth+1))
            {
              case TRUE:
                if (debugEnabled())
                {
                  TRACER.debugVerbose(
                    "Returning TRUE for OR component %s in filter " +
                    "%s for entry %s",
                    f, completeFilter, entry.getDN());
                }
                return ConditionResult.TRUE;
              case FALSE:
                break;
              case UNDEFINED:
                if (debugEnabled())
                {
                  TRACER.debugInfo(
                  "Undefined result for OR component %s in filter " +
                  "%s for entry %s",
                  f, completeFilter, entry.getDN());
                }
                result = ConditionResult.UNDEFINED;
                break;
              default:
                Message message =
                    ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.
                      get(String.valueOf(entry.getDN()),
                          String.valueOf(completeFilter),
                          String.valueOf(result));
                throw new
                     DirectoryException(
                          DirectoryServer.getServerErrorResultCode(),
                          message);
            }
          }
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning %s for OR component %s in filter %s for " +
                "entry %s", result, this, completeFilter,
                            entry.getDN());
          }
          return result;
        }
        return processOR(completeFilter, entry, depth);
      case NOT:
        if (notComponent == null)
        {
          // The NOT subcomponent was null.  This is not allowed.
          Message message = ERR_SEARCH_FILTER_NOT_COMPONENT_NULL.
              get(String.valueOf(entry.getDN()),
                  String.valueOf(completeFilter));
          throw new DirectoryException(
                         DirectoryServer.getServerErrorResultCode(),
                         message);
        }
        else
        {
          // The subcomponent for the NOT filter can be an AND, OR, or
          // NOT filter that would require more nesting.  Make sure
          // that we don't go too deep.
          if (depth >= MAX_NESTED_FILTER_DEPTH)
          {
            Message message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP.
                get(String.valueOf(entry.getDN()),
                    String.valueOf(completeFilter));
            throw new DirectoryException(
                           DirectoryServer.getServerErrorResultCode(),
                           message);
          }
          ConditionResult result =
               notComponent.matchesEntryInternal(completeFilter,
                                                 entry, depth+1);
          switch (result)
          {
            case TRUE:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                   "Returning FALSE for NOT component %s in filter " +
                   "%s for entry %s",
                   notComponent, completeFilter, entry.getDN());
              }
              return ConditionResult.FALSE;
            case FALSE:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Returning TRUE for NOT component %s in filter " +
                    "%s for entry %s",
                    notComponent, completeFilter, entry.getDN());
              }
              return ConditionResult.TRUE;
            case UNDEFINED:
              if (debugEnabled())
              {
                TRACER.debugInfo(
                  "Undefined result for NOT component %s in filter " +
                  "%s for entry %s",
                  notComponent, completeFilter, entry.getDN());
              }
              return ConditionResult.UNDEFINED;
            default:
              Message message = ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.
                  get(String.valueOf(entry.getDN()),
                      String.valueOf(completeFilter),
                      String.valueOf(result));
              throw new
                   DirectoryException(
                        DirectoryServer.getServerErrorResultCode(),
                        message);
          }
        }
        return processNOT(completeFilter, entry, depth);
      case EQUALITY:
        // Make sure that an attribute type has been defined.
        if (attributeType == null)
        {
          Message message =
              ERR_SEARCH_FILTER_EQUALITY_NO_ATTRIBUTE_TYPE.
                get(String.valueOf(entry.getDN()), toString());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // Make sure that an assertion value has been defined.
        if (assertionValue == null)
        {
          Message message =
              ERR_SEARCH_FILTER_EQUALITY_NO_ASSERTION_VALUE.
                get(String.valueOf(entry.getDN()), toString(),
                    attributeType.getNameOrOID());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // See if the entry has an attribute with the requested type.
        List<Attribute> attrs = entry.getAttribute(attributeType,
                                                   attributeOptions);
        if ((attrs == null) || (attrs.isEmpty()))
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning FALSE for equality component %s in " +
                "filter %s because entry %s didn't have attribute " +
                "type %s",
                         this, completeFilter, entry.getDN(),
                         attributeType.getNameOrOID());
          }
          return ConditionResult.FALSE;
        }
        // Iterate through all the attributes and see if we can find a
        // match.
        for (Attribute a : attrs)
        {
          if (a.hasValue(assertionValue))
          {
            if (debugEnabled())
            {
              TRACER.debugVerbose(
                  "Returning TRUE for equality component %s in " +
                  "filter %s for entry %s",
                           this, completeFilter, entry.getDN());
            }
            return ConditionResult.TRUE;
          }
        }
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning FALSE for equality component %s in filter " +
              "%s because entry %s didn't have attribute type " +
              "%s with value %s",
                       this, completeFilter, entry.getDN(),
                       attributeType.getNameOrOID(),
                       assertionValue.getStringValue());
        }
        return ConditionResult.FALSE;
        return processEquality(completeFilter, entry);
      case SUBSTRING:
        // Make sure that an attribute type has been defined.
        if (attributeType == null)
        {
          Message message =
              ERR_SEARCH_FILTER_SUBSTRING_NO_ATTRIBUTE_TYPE.
                get(String.valueOf(entry.getDN()), toString());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // Make sure that at least one substring element has been
        // defined.
        if ((subInitialElement == null) &&
            (subFinalElement == null) &&
            ((subAnyElements == null) || subAnyElements.isEmpty()))
        {
          Message message =
              ERR_SEARCH_FILTER_SUBSTRING_NO_SUBSTRING_COMPONENTS.
                get(String.valueOf(entry.getDN()), toString(),
                    attributeType.getNameOrOID());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // See if the entry has an attribute with the requested type.
        attrs = entry.getAttribute(attributeType, attributeOptions);
        if ((attrs == null) || (attrs.isEmpty()))
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning FALSE for substring component %s in " +
                "filter %s because entry %s didn't have attribute " +
                "type %s",
                         this, completeFilter, entry.getDN(),
                         attributeType.getNameOrOID());
          }
          return ConditionResult.FALSE;
        }
        // Iterate through all the attributes and see if we can find a
        // match.
        ConditionResult result = ConditionResult.FALSE;
        for (Attribute a : attrs)
        {
          switch (a.matchesSubstring(subInitialElement,
                                     subAnyElements,
                                     subFinalElement))
          {
            case TRUE:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Returning TRUE for substring component %s in " +
                    "filter %s for entry %s",
                             this, completeFilter, entry.getDN());
              }
              return ConditionResult.TRUE;
            case FALSE:
              break;
            case UNDEFINED:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Undefined result encountered for substring " +
                    "component %s in filter %s for entry %s",
                             this, completeFilter, entry.getDN());
              }
              result = ConditionResult.UNDEFINED;
              break;
            default:
          }
        }
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning %s for substring component %s in filter " +
              "%s for entry %s",
              result, this, completeFilter, entry.getDN());
        }
        return result;
        return processSubstring(completeFilter, entry);
      case GREATER_OR_EQUAL:
        // Make sure that an attribute type has been defined.
        if (attributeType == null)
        {
          Message message =
              ERR_SEARCH_FILTER_GREATER_OR_EQUAL_NO_ATTRIBUTE_TYPE.
                get(String.valueOf(entry.getDN()), toString());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // Make sure that an assertion value has been defined.
        if (assertionValue == null)
        {
          Message message =
              ERR_SEARCH_FILTER_GREATER_OR_EQUAL_NO_VALUE.
                get(String.valueOf(entry.getDN()), toString(),
                    attributeType.getNameOrOID());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // See if the entry has an attribute with the requested type.
        attrs = entry.getAttribute(attributeType, attributeOptions);
        if ((attrs == null) || (attrs.isEmpty()))
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose("Returning FALSE for " +
                "greater-or-equal component %s in filter %s " +
                "because entry %s didn't have attribute type %s",
                         this, completeFilter, entry.getDN(),
                         attributeType.getNameOrOID());
          }
          return ConditionResult.FALSE;
        }
        // Iterate through all the attributes and see if we can find a
        // match.
        result = ConditionResult.FALSE;
        for (Attribute a : attrs)
        {
          switch (a.greaterThanOrEqualTo(assertionValue))
          {
            case TRUE:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Returning TRUE for greater-or-equal component " +
                    "%s in filter %s for entry %s",
                             this, completeFilter, entry.getDN());
              }
              return ConditionResult.TRUE;
            case FALSE:
              break;
            case UNDEFINED:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Undefined result encountered for " +
                    "greater-or-equal component %s in filter %s " +
                    "for entry %s", this, completeFilter,
                    entry.getDN());
              }
              result = ConditionResult.UNDEFINED;
              break;
            default:
          }
        }
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning %s for greater-or-equal component %s in " +
              "filter %s for entry %s",
                       result, this, completeFilter, entry.getDN());
        }
        return result;
        return processGreaterOrEqual(completeFilter, entry);
      case LESS_OR_EQUAL:
        // Make sure that an attribute type has been defined.
        if (attributeType == null)
        {
          Message message =
              ERR_SEARCH_FILTER_LESS_OR_EQUAL_NO_ATTRIBUTE_TYPE.
                get(String.valueOf(entry.getDN()), toString());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // Make sure that an assertion value has been defined.
        if (assertionValue == null)
        {
          Message message =
              ERR_SEARCH_FILTER_LESS_OR_EQUAL_NO_ASSERTION_VALUE.
                get(String.valueOf(entry.getDN()), toString(),
                    attributeType.getNameOrOID());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // See if the entry has an attribute with the requested type.
        attrs = entry.getAttribute(attributeType, attributeOptions);
        if ((attrs == null) || (attrs.isEmpty()))
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning FALSE for less-or-equal component %s in " +
                "filter %s because entry %s didn't have attribute " +
                "type %s", this, completeFilter, entry.getDN(),
                           attributeType.getNameOrOID());
          }
          return ConditionResult.FALSE;
        }
        // Iterate through all the attributes and see if we can find a
        // match.
        result = ConditionResult.FALSE;
        for (Attribute a : attrs)
        {
          switch (a.lessThanOrEqualTo(assertionValue))
          {
            case TRUE:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Returning TRUE for less-or-equal component %s " +
                    "in filter %s for entry %s",
                             this, completeFilter, entry.getDN());
              }
              return ConditionResult.TRUE;
            case FALSE:
              break;
            case UNDEFINED:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Undefined result encountered for " +
                        "less-or-equal component %s in filter %s " +
                        "for entry %s",
                        this, completeFilter, entry.getDN());
              }
              result = ConditionResult.UNDEFINED;
              break;
            default:
          }
        }
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning %s for less-or-equal component %s in " +
              "filter %s for entry %s",
                       result, this, completeFilter, entry.getDN());
        }
        return result;
        return processLessOrEqual(completeFilter, entry);
      case PRESENT:
        // Make sure that an attribute type has been defined.
        if (attributeType == null)
        {
          Message message =
              ERR_SEARCH_FILTER_PRESENCE_NO_ATTRIBUTE_TYPE.
                get(String.valueOf(entry.getDN()), toString());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // See if the entry has an attribute with the requested type.
        // If so, then it's a match.  If not, then it's not a match.
        if (entry.hasAttribute(attributeType, attributeOptions))
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning TRUE for presence component %s in " +
                "filter %s for entry %s",
                this, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        }
        else
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning FALSE for presence component %s in " +
                "filter %s for entry %s",
                this, completeFilter, entry.getDN());
          }
          return ConditionResult.FALSE;
        }
        return processPresent(completeFilter, entry);
      case APPROXIMATE_MATCH:
        // Make sure that an attribute type has been defined.
        if (attributeType == null)
        {
          Message message =
              ERR_SEARCH_FILTER_APPROXIMATE_NO_ATTRIBUTE_TYPE.
                get(String.valueOf(entry.getDN()), toString());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // Make sure that an assertion value has been defined.
        if (assertionValue == null)
        {
          Message message =
              ERR_SEARCH_FILTER_APPROXIMATE_NO_ASSERTION_VALUE.
                get(String.valueOf(entry.getDN()), toString(),
                    attributeType.getNameOrOID());
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message);
        }
        // See if the entry has an attribute with the requested type.
        attrs = entry.getAttribute(attributeType, attributeOptions);
        if ((attrs == null) || (attrs.isEmpty()))
        {
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning FALSE for approximate component %s in " +
                "filter %s because entry %s didn't have attribute " +
                "type %s", this, completeFilter, entry.getDN(),
                           attributeType.getNameOrOID());
          }
          return ConditionResult.FALSE;
        }
        // Iterate through all the attributes and see if we can find a
        // match.
        result = ConditionResult.FALSE;
        for (Attribute a : attrs)
        {
          switch (a.approximatelyEqualTo(assertionValue))
          {
            case TRUE:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                   "Returning TRUE for approximate component %s in " +
                   "filter %s for entry %s",
                   this, completeFilter, entry.getDN());
              }
              return ConditionResult.TRUE;
            case FALSE:
              break;
            case UNDEFINED:
              if (debugEnabled())
              {
                TRACER.debugVerbose(
                    "Undefined result encountered for approximate " +
                    "component %s in filter %s for entry %s",
                             this, completeFilter, entry.getDN());
              }
              result = ConditionResult.UNDEFINED;
              break;
            default:
          }
        }
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning %s for approximate component %s in filter " +
              "%s for entry %s",
              result, this, completeFilter, entry.getDN());
        }
        return result;
        return processApproximate(completeFilter, entry);
      case EXTENSIBLE_MATCH:
        return processExtensibleMatch(completeFilter, entry);
@@ -3034,6 +2374,902 @@
  /**
   * Indicates whether the this AND filter matches the provided entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   * @param  depth           The current depth of the evaluation,
   *                         which is used to prevent infinite
   *                         recursion due to highly nested filters
   *                         and eventually running out of stack
   *                         space.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processAND(SearchFilter completeFilter,
                                     Entry entry, int depth)
          throws DirectoryException
  {
    if (filterComponents == null)
    {
      // The set of subcomponents was null.  This is not allowed.
      Message message =
          ERR_SEARCH_FILTER_COMPOUND_COMPONENTS_NULL.
            get(String.valueOf(entry.getDN()),
                String.valueOf(completeFilter),
                String.valueOf(filterType));
      throw new DirectoryException(
                     DirectoryServer.getServerErrorResultCode(),
                     message);
    }
    else if (filterComponents.isEmpty())
    {
      // An AND filter with no elements like "(&)" is specified as
      // "undefined" in RFC 2251, but is considered one of the
      // TRUE/FALSE filters in RFC 4526, in which case we should
      // always return true.
      if (debugEnabled())
      {
        TRACER.debugInfo("Returning TRUE for LDAP TRUE " +
            "filter (&)");
      }
      return ConditionResult.TRUE;
    }
    else
    {
      // We will have to evaluate one or more subcomponents.  In
      // this case, first check our depth to make sure we're not
      // nesting too deep.
      if (depth >= MAX_NESTED_FILTER_DEPTH)
      {
        Message message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP.
            get(String.valueOf(entry.getDN()),
                String.valueOf(completeFilter));
        throw new DirectoryException(
                       DirectoryServer.getServerErrorResultCode(),
                       message);
      }
      for (SearchFilter f : filterComponents)
      {
        ConditionResult result =
             f.matchesEntryInternal(completeFilter, entry,
                                 depth+1);
        switch (result)
        {
          case TRUE:
            break;
          case FALSE:
            if (debugEnabled())
            {
              TRACER.debugVerbose(
                  "Returning FALSE for AND component %s in " +
                  "filter %s for entry %s",
                           f, completeFilter, entry.getDN());
            }
            return result;
          case UNDEFINED:
            if (debugEnabled())
            {
              TRACER.debugInfo(
             "Undefined result for AND component %s in filter " +
             "%s for entry %s", f, completeFilter, entry.getDN());
            }
            return result;
          default:
            Message message =
                ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.
                  get(String.valueOf(entry.getDN()),
                      String.valueOf(completeFilter),
                      String.valueOf(result));
            throw new
                 DirectoryException(
                      DirectoryServer.getServerErrorResultCode(),
                      message);
        }
      }
      // If we have gotten here, then all the components must have
      // matched.
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning TRUE for AND component %s in filter %s " +
            "for entry %s", this, completeFilter, entry.getDN());
      }
      return ConditionResult.TRUE;
    }
  }
  /**
   * Indicates whether the this OR filter matches the provided entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   * @param  depth           The current depth of the evaluation,
   *                         which is used to prevent infinite
   *                         recursion due to highly nested filters
   *                         and eventually running out of stack
   *                         space.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processOR(SearchFilter completeFilter,
                                    Entry entry, int depth)
          throws DirectoryException
  {
    if (filterComponents == null)
    {
      // The set of subcomponents was null.  This is not allowed.
      Message message =
          ERR_SEARCH_FILTER_COMPOUND_COMPONENTS_NULL.
            get(String.valueOf(entry.getDN()),
                String.valueOf(completeFilter),
                String.valueOf(filterType));
      throw new DirectoryException(
                     DirectoryServer.getServerErrorResultCode(),
                     message);
    }
    else if (filterComponents.isEmpty())
    {
      // An OR filter with no elements like "(|)" is specified as
      // "undefined" in RFC 2251, but is considered one of the
      // TRUE/FALSE filters in RFC 4526, in which case we should
      // always return false.
      if (debugEnabled())
      {
        TRACER.debugInfo("Returning FALSE for LDAP FALSE " +
            "filter (|)");
      }
      return ConditionResult.FALSE;
    }
    else
    {
      // We will have to evaluate one or more subcomponents.  In
      // this case, first check our depth to make sure we're not
      // nesting too deep.
      if (depth >= MAX_NESTED_FILTER_DEPTH)
      {
        Message message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP.
            get(String.valueOf(entry.getDN()),
                String.valueOf(completeFilter));
        throw new DirectoryException(
                       DirectoryServer.getServerErrorResultCode(),
                       message);
      }
      ConditionResult result = ConditionResult.FALSE;
      for (SearchFilter f : filterComponents)
      {
        switch (f.matchesEntryInternal(completeFilter, entry,
                               depth+1))
        {
          case TRUE:
            if (debugEnabled())
            {
              TRACER.debugVerbose(
                "Returning TRUE for OR component %s in filter " +
                "%s for entry %s",
                f, completeFilter, entry.getDN());
            }
            return ConditionResult.TRUE;
          case FALSE:
            break;
          case UNDEFINED:
            if (debugEnabled())
            {
              TRACER.debugInfo(
              "Undefined result for OR component %s in filter " +
              "%s for entry %s",
              f, completeFilter, entry.getDN());
            }
            result = ConditionResult.UNDEFINED;
            break;
          default:
            Message message =
                ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.
                  get(String.valueOf(entry.getDN()),
                      String.valueOf(completeFilter),
                      String.valueOf(result));
            throw new
                 DirectoryException(
                      DirectoryServer.getServerErrorResultCode(),
                      message);
        }
      }
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning %s for OR component %s in filter %s for " +
            "entry %s", result, this, completeFilter,
                        entry.getDN());
      }
      return result;
    }
  }
  /**
   * Indicates whether the this NOT filter matches the provided entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   * @param  depth           The current depth of the evaluation,
   *                         which is used to prevent infinite
   *                         recursion due to highly nested filters
   *                         and eventually running out of stack
   *                         space.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processNOT(SearchFilter completeFilter,
                                     Entry entry, int depth)
          throws DirectoryException
  {
    if (notComponent == null)
    {
      // The NOT subcomponent was null.  This is not allowed.
      Message message = ERR_SEARCH_FILTER_NOT_COMPONENT_NULL.
          get(String.valueOf(entry.getDN()),
              String.valueOf(completeFilter));
      throw new DirectoryException(
                     DirectoryServer.getServerErrorResultCode(),
                     message);
    }
    else
    {
      // The subcomponent for the NOT filter can be an AND, OR, or
      // NOT filter that would require more nesting.  Make sure
      // that we don't go too deep.
      if (depth >= MAX_NESTED_FILTER_DEPTH)
      {
        Message message = ERR_SEARCH_FILTER_NESTED_TOO_DEEP.
            get(String.valueOf(entry.getDN()),
                String.valueOf(completeFilter));
        throw new DirectoryException(
                       DirectoryServer.getServerErrorResultCode(),
                       message);
      }
      ConditionResult result =
           notComponent.matchesEntryInternal(completeFilter,
                                             entry, depth+1);
      switch (result)
      {
        case TRUE:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
               "Returning FALSE for NOT component %s in filter " +
               "%s for entry %s",
               notComponent, completeFilter, entry.getDN());
          }
          return ConditionResult.FALSE;
        case FALSE:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning TRUE for NOT component %s in filter " +
                "%s for entry %s",
                notComponent, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        case UNDEFINED:
          if (debugEnabled())
          {
            TRACER.debugInfo(
              "Undefined result for NOT component %s in filter " +
              "%s for entry %s",
              notComponent, completeFilter, entry.getDN());
          }
          return ConditionResult.UNDEFINED;
        default:
          Message message = ERR_SEARCH_FILTER_INVALID_RESULT_TYPE.
              get(String.valueOf(entry.getDN()),
                  String.valueOf(completeFilter),
                  String.valueOf(result));
          throw new
               DirectoryException(
                    DirectoryServer.getServerErrorResultCode(),
                    message);
      }
    }
  }
  /**
   * Indicates whether the this equality filter matches the provided
   * entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processEquality(SearchFilter completeFilter,
                                          Entry entry)
          throws DirectoryException
  {
    // Make sure that an attribute type has been defined.
    if (attributeType == null)
    {
      Message message =
          ERR_SEARCH_FILTER_EQUALITY_NO_ATTRIBUTE_TYPE.
            get(String.valueOf(entry.getDN()), toString());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // Make sure that an assertion value has been defined.
    if (assertionValue == null)
    {
      Message message =
          ERR_SEARCH_FILTER_EQUALITY_NO_ASSERTION_VALUE.
            get(String.valueOf(entry.getDN()), toString(),
                attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // See if the entry has an attribute with the requested type.
    List<Attribute> attrs = entry.getAttribute(attributeType,
                                               attributeOptions);
    if ((attrs == null) || (attrs.isEmpty()))
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning FALSE for equality component %s in " +
            "filter %s because entry %s didn't have attribute " +
            "type %s",
                     this, completeFilter, entry.getDN(),
                     attributeType.getNameOrOID());
      }
      return ConditionResult.FALSE;
    }
    // Iterate through all the attributes and see if we can find a
    // match.
    for (Attribute a : attrs)
    {
      if (a.hasValue(assertionValue))
      {
        if (debugEnabled())
        {
          TRACER.debugVerbose(
              "Returning TRUE for equality component %s in " +
              "filter %s for entry %s",
                       this, completeFilter, entry.getDN());
        }
        return ConditionResult.TRUE;
      }
    }
    if (debugEnabled())
    {
      TRACER.debugVerbose(
          "Returning FALSE for equality component %s in filter " +
          "%s because entry %s didn't have attribute type " +
          "%s with value %s",
                   this, completeFilter, entry.getDN(),
                   attributeType.getNameOrOID(),
                   assertionValue.getStringValue());
    }
    return ConditionResult.FALSE;
  }
  /**
   * Indicates whether the this substring filter matches the provided
   * entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processSubstring(
                                SearchFilter completeFilter,
                                Entry entry)
          throws DirectoryException
  {
    // Make sure that an attribute type has been defined.
    if (attributeType == null)
    {
      Message message =
          ERR_SEARCH_FILTER_SUBSTRING_NO_ATTRIBUTE_TYPE.
            get(String.valueOf(entry.getDN()), toString());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // Make sure that at least one substring element has been
    // defined.
    if ((subInitialElement == null) &&
        (subFinalElement == null) &&
        ((subAnyElements == null) || subAnyElements.isEmpty()))
    {
      Message message =
          ERR_SEARCH_FILTER_SUBSTRING_NO_SUBSTRING_COMPONENTS.
            get(String.valueOf(entry.getDN()), toString(),
                attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // See if the entry has an attribute with the requested type.
    List<Attribute> attrs =
         entry.getAttribute(attributeType, attributeOptions);
    if ((attrs == null) || (attrs.isEmpty()))
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning FALSE for substring component %s in " +
            "filter %s because entry %s didn't have attribute " +
            "type %s",
                     this, completeFilter, entry.getDN(),
                     attributeType.getNameOrOID());
      }
      return ConditionResult.FALSE;
    }
    // Iterate through all the attributes and see if we can find a
    // match.
    ConditionResult result = ConditionResult.FALSE;
    for (Attribute a : attrs)
    {
      switch (a.matchesSubstring(subInitialElement,
                                 subAnyElements,
                                 subFinalElement))
      {
        case TRUE:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning TRUE for substring component %s in " +
                "filter %s for entry %s",
                         this, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        case FALSE:
          break;
        case UNDEFINED:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Undefined result encountered for substring " +
                "component %s in filter %s for entry %s",
                         this, completeFilter, entry.getDN());
          }
          result = ConditionResult.UNDEFINED;
          break;
        default:
      }
    }
    if (debugEnabled())
    {
      TRACER.debugVerbose(
          "Returning %s for substring component %s in filter " +
          "%s for entry %s",
          result, this, completeFilter, entry.getDN());
    }
    return result;
  }
  /**
   * Indicates whether the this greater-or-equal filter matches the
   * provided entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processGreaterOrEqual(
                                SearchFilter completeFilter,
                                Entry entry)
          throws DirectoryException
  {
    // Make sure that an attribute type has been defined.
    if (attributeType == null)
    {
      Message message =
          ERR_SEARCH_FILTER_GREATER_OR_EQUAL_NO_ATTRIBUTE_TYPE.
            get(String.valueOf(entry.getDN()), toString());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // Make sure that an assertion value has been defined.
    if (assertionValue == null)
    {
      Message message =
          ERR_SEARCH_FILTER_GREATER_OR_EQUAL_NO_VALUE.
            get(String.valueOf(entry.getDN()), toString(),
                attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // See if the entry has an attribute with the requested type.
    List<Attribute> attrs =
         entry.getAttribute(attributeType, attributeOptions);
    if ((attrs == null) || (attrs.isEmpty()))
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose("Returning FALSE for " +
            "greater-or-equal component %s in filter %s " +
            "because entry %s didn't have attribute type %s",
                     this, completeFilter, entry.getDN(),
                     attributeType.getNameOrOID());
      }
      return ConditionResult.FALSE;
    }
    // Iterate through all the attributes and see if we can find a
    // match.
    ConditionResult result = ConditionResult.FALSE;
    for (Attribute a : attrs)
    {
      switch (a.greaterThanOrEqualTo(assertionValue))
      {
        case TRUE:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning TRUE for greater-or-equal component " +
                "%s in filter %s for entry %s",
                         this, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        case FALSE:
          break;
        case UNDEFINED:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Undefined result encountered for " +
                "greater-or-equal component %s in filter %s " +
                "for entry %s", this, completeFilter,
                entry.getDN());
          }
          result = ConditionResult.UNDEFINED;
          break;
        default:
      }
    }
    if (debugEnabled())
    {
      TRACER.debugVerbose(
          "Returning %s for greater-or-equal component %s in " +
          "filter %s for entry %s",
                   result, this, completeFilter, entry.getDN());
    }
    return result;
  }
  /**
   * Indicates whether the this less-or-equal filter matches the
   * provided entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processLessOrEqual(
                                SearchFilter completeFilter,
                                Entry entry)
          throws DirectoryException
  {
    // Make sure that an attribute type has been defined.
    if (attributeType == null)
    {
      Message message =
          ERR_SEARCH_FILTER_LESS_OR_EQUAL_NO_ATTRIBUTE_TYPE.
            get(String.valueOf(entry.getDN()), toString());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // Make sure that an assertion value has been defined.
    if (assertionValue == null)
    {
      Message message =
          ERR_SEARCH_FILTER_LESS_OR_EQUAL_NO_ASSERTION_VALUE.
            get(String.valueOf(entry.getDN()), toString(),
                attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // See if the entry has an attribute with the requested type.
    List<Attribute> attrs =
         entry.getAttribute(attributeType, attributeOptions);
    if ((attrs == null) || (attrs.isEmpty()))
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning FALSE for less-or-equal component %s in " +
            "filter %s because entry %s didn't have attribute " +
            "type %s", this, completeFilter, entry.getDN(),
                       attributeType.getNameOrOID());
      }
      return ConditionResult.FALSE;
    }
    // Iterate through all the attributes and see if we can find a
    // match.
    ConditionResult result = ConditionResult.FALSE;
    for (Attribute a : attrs)
    {
      switch (a.lessThanOrEqualTo(assertionValue))
      {
        case TRUE:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Returning TRUE for less-or-equal component %s " +
                "in filter %s for entry %s",
                         this, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        case FALSE:
          break;
        case UNDEFINED:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Undefined result encountered for " +
                    "less-or-equal component %s in filter %s " +
                    "for entry %s",
                    this, completeFilter, entry.getDN());
          }
          result = ConditionResult.UNDEFINED;
          break;
        default:
      }
    }
    if (debugEnabled())
    {
      TRACER.debugVerbose(
          "Returning %s for less-or-equal component %s in " +
          "filter %s for entry %s",
                   result, this, completeFilter, entry.getDN());
    }
    return result;
  }
  /**
   * Indicates whether the this present filter matches the provided
   * entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processPresent(SearchFilter completeFilter,
                                         Entry entry)
          throws DirectoryException
  {
    // Make sure that an attribute type has been defined.
    if (attributeType == null)
    {
      Message message =
          ERR_SEARCH_FILTER_PRESENCE_NO_ATTRIBUTE_TYPE.
            get(String.valueOf(entry.getDN()), toString());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // See if the entry has an attribute with the requested type.
    // If so, then it's a match.  If not, then it's not a match.
    if (entry.hasAttribute(attributeType, attributeOptions))
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning TRUE for presence component %s in " +
            "filter %s for entry %s",
            this, completeFilter, entry.getDN());
      }
      return ConditionResult.TRUE;
    }
    else
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning FALSE for presence component %s in " +
            "filter %s for entry %s",
            this, completeFilter, entry.getDN());
      }
      return ConditionResult.FALSE;
    }
  }
  /**
   * Indicates whether the this approximate filter matches the
   * provided entry.
   *
   * @param  completeFilter  The complete filter being checked, of
   *                         which this filter may be a subset.
   * @param  entry           The entry for which to make the
   *                         determination.
   *
   * @return  <CODE>TRUE</CODE> if this filter matches the provided
   *          entry, <CODE>FALSE</CODE> if it does not, or
   *          <CODE>UNDEFINED</CODE> if the result is undefined.
   *
   * @throws  DirectoryException  If a problem is encountered during
   *                              processing.
   */
  private ConditionResult processApproximate(
                                SearchFilter completeFilter,
                                Entry entry)
          throws DirectoryException
  {
    // Make sure that an attribute type has been defined.
    if (attributeType == null)
    {
      Message message =
          ERR_SEARCH_FILTER_APPROXIMATE_NO_ATTRIBUTE_TYPE.
            get(String.valueOf(entry.getDN()), toString());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // Make sure that an assertion value has been defined.
    if (assertionValue == null)
    {
      Message message =
          ERR_SEARCH_FILTER_APPROXIMATE_NO_ASSERTION_VALUE.
            get(String.valueOf(entry.getDN()), toString(),
                attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                   message);
    }
    // See if the entry has an attribute with the requested type.
    List<Attribute> attrs =
         entry.getAttribute(attributeType, attributeOptions);
    if ((attrs == null) || (attrs.isEmpty()))
    {
      if (debugEnabled())
      {
        TRACER.debugVerbose(
            "Returning FALSE for approximate component %s in " +
            "filter %s because entry %s didn't have attribute " +
            "type %s", this, completeFilter, entry.getDN(),
                       attributeType.getNameOrOID());
      }
      return ConditionResult.FALSE;
    }
    // Iterate through all the attributes and see if we can find a
    // match.
    ConditionResult result = ConditionResult.FALSE;
    for (Attribute a : attrs)
    {
      switch (a.approximatelyEqualTo(assertionValue))
      {
        case TRUE:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
               "Returning TRUE for approximate component %s in " +
               "filter %s for entry %s",
               this, completeFilter, entry.getDN());
          }
          return ConditionResult.TRUE;
        case FALSE:
          break;
        case UNDEFINED:
          if (debugEnabled())
          {
            TRACER.debugVerbose(
                "Undefined result encountered for approximate " +
                "component %s in filter %s for entry %s",
                         this, completeFilter, entry.getDN());
          }
          result = ConditionResult.UNDEFINED;
          break;
        default:
      }
    }
    if (debugEnabled())
    {
      TRACER.debugVerbose(
          "Returning %s for approximate component %s in filter " +
          "%s for entry %s",
          result, this, completeFilter, entry.getDN());
    }
    return result;
  }
  /**
   * Indicates whether this extensibleMatch filter matches the
   * provided entry.
   *
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -193,7 +193,7 @@
    skipPostOperation = false;
    // Check for a request to cancel this operation.
    if (getCancelRequest() != null)
    if (cancelIfRequested())
    {
      return;
    }
@@ -222,7 +222,7 @@
      }
      // Check for a request to cancel this operation.
      if (getCancelRequest() != null)
      if (cancelIfRequested())
      {
        return;
      }
@@ -254,7 +254,7 @@
      try
      {
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -596,7 +596,7 @@
        }
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -631,7 +631,7 @@
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -855,6 +855,27 @@
  /**
   * Checks to determine whether there has been a request to cancel this
   * operation.  If so, then set the cancel result and processing stop time.
   *
   * @return  {@code true} if there was a cancel request, or {@code false} if
   *          not.
   */
  private boolean cancelIfRequested()
  {
    if (getCancelRequest() == null)
    {
      return false;
    }
    indicateCancelled(getCancelRequest());
    setProcessingStopTime();
    return true;
  }
  /**
   * Acquire a read lock on the parent of the entry to add.
   *
   * @return  The acquired read lock.
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -152,7 +152,7 @@
    // Check for a request to cancel this operation.
    if (getCancelRequest() != null)
    if (cancelIfRequested())
    {
      return;
    }
@@ -185,7 +185,7 @@
      // Check for a request to cancel this operation.
      if (getCancelRequest() != null)
      if (cancelIfRequested())
      {
        return;
      }
@@ -302,7 +302,7 @@
        }
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -415,7 +415,7 @@
    // Check for a request to cancel this operation.
    if (getCancelRequest() != null)
    if (cancelIfRequested())
    {
      return;
    }
@@ -438,6 +438,27 @@
  /**
   * Checks to determine whether there has been a request to cancel this
   * operation.  If so, then set the cancel result and processing stop time.
   *
   * @return  {@code true} if there was a cancel request, or {@code false} if
   *          not.
   */
  private boolean cancelIfRequested()
  {
    if (getCancelRequest() == null)
    {
      return false;
    }
    indicateCancelled(getCancelRequest());
    setProcessingStopTime();
    return true;
  }
  /**
   * Performs any processing required for the controls included in the request.
   *
   * @throws  DirectoryException  If a problem occurs that should prevent the
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -163,7 +163,7 @@
    skipPostOperation = false;
    // Check for a request to cancel this operation.
    if (getCancelRequest() != null)
    if (cancelIfRequested())
    {
      return;
    }
@@ -314,7 +314,7 @@
        }
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -345,7 +345,7 @@
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -590,6 +590,27 @@
  /**
   * Checks to determine whether there has been a request to cancel this
   * operation.  If so, then set the cancel result and processing stop time.
   *
   * @return  {@code true} if there was a cancel request, or {@code false} if
   *          not.
   */
  private boolean cancelIfRequested()
  {
    if (getCancelRequest() == null)
    {
      return false;
    }
    indicateCancelled(getCancelRequest());
    setProcessingStopTime();
    return true;
  }
  /**
   * Performs any request control processing needed for this operation.
   *
   * @throws  DirectoryException  If a problem occurs that should cause the
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -203,7 +203,7 @@
    skipPostOperation = false;
    // Check for a request to cancel this operation.
    if (getCancelRequest() != null)
    if (cancelIfRequested())
    {
      return;
    }
@@ -283,7 +283,7 @@
      // Check for a request to cancel this operation.
      if (getCancelRequest() != null)
      if (cancelIfRequested())
      {
        return;
      }
@@ -359,7 +359,7 @@
      try
      {
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -514,7 +514,7 @@
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -575,7 +575,7 @@
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -789,6 +789,27 @@
  /**
   * Checks to determine whether there has been a request to cancel this
   * operation.  If so, then set the cancel result and processing stop time.
   *
   * @return  {@code true} if there was a cancel request, or {@code false} if
   *          not.
   */
  private boolean cancelIfRequested()
  {
    if (getCancelRequest() == null)
    {
      return false;
    }
    indicateCancelled(getCancelRequest());
    setProcessingStopTime();
    return true;
  }
  /**
   * Processes the set of controls included in the request.
   *
   * @throws  DirectoryException  If a problem occurs that should cause the
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -366,7 +366,7 @@
      // Check for a request to cancel this operation.
      if (getCancelRequest() != null)
      if (cancelIfRequested())
      {
        return;
      }
@@ -395,86 +395,26 @@
      try
      {
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
        // Get the entry to modify.  If it does not exist, then fail.
        try
        {
          currentEntry = backend.getEntry(entryDN);
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, de);
          }
          // Get the entry to modify.  If it does not exist, then fail.
          getEntryToModify();
          setResponseData(de);
          break modifyProcessing;
        }
        if (currentEntry == null)
        {
          setResultCode(ResultCode.NO_SUCH_OBJECT);
          appendErrorMessage(ERR_MODIFY_NO_SUCH_ENTRY.get(
                                  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 modifyProcessing;
        }
        // Check to see if there are any controls in the request.  If so, then
        // see if there is any special processing required.
        try
        {
          // Check to see if there are any controls in the request.  If so, then
          // see if there is any special processing required.
          processRequestControls();
        }
        catch (DirectoryException de)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, de);
          }
          setResponseData(de);
          break modifyProcessing;
        }
          // Get the password policy state object for the entry that can be used
          // to perform any appropriate password policy processing.  Also, see
          // if the entry is being updated by the end user or an administrator.
          selfChange = entryDN.equals(getAuthorizationDN());
        // Get the password policy state object for the entry that can be used
        // to perform any appropriate password policy processing.  Also, see if
        // the entry is being updated by the end user or an administrator.
        selfChange = entryDN.equals(getAuthorizationDN());
        try
        {
          // FIXME -- Need a way to enable debug mode.
          pwPolicyState = new PasswordPolicyState(currentEntry, false, false);
        }
@@ -485,8 +425,7 @@
            TRACER.debugCaught(DebugLogLevel.ERROR, de);
          }
          setResultCode(de.getResultCode());
          appendErrorMessage(de.getMessageObject());
          setResponseData(de);
          break modifyProcessing;
        }
@@ -529,6 +468,12 @@
        try
        {
          handleInitialPasswordPolicyAndSchemaProcessing();
          wasLocked = false;
          if (passwordChanged)
          {
            performAdditionalPasswordChangedProcessing();
          }
        }
        catch (DirectoryException de)
        {
@@ -542,28 +487,6 @@
        }
        // If there was a password change, then perform any additional checks
        // that may be necessary.
        wasLocked = false;
        if (passwordChanged)
        {
          try
          {
            performAdditionalPasswordChangedProcessing();
          }
          catch (DirectoryException de)
          {
            if (debugEnabled())
            {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }
            setResponseData(de);
            break modifyProcessing;
          }
        }
        // Check to see if the client has permission to perform the modify.
        // The access control check is not made any earlier because the handler
        // needs access to the modified entry.
@@ -614,7 +537,7 @@
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -648,7 +571,7 @@
        // Check for a request to cancel this operation.
        if (getCancelRequest() != null)
        if (cancelIfRequested())
        {
          return;
        }
@@ -761,7 +684,7 @@
          setResultCode(cancelResult.getResultCode());
          Message message = coe.getMessageObject();
          if ((message != null) && (message.length() > 0))
          if (message != null)
          {
            appendErrorMessage(message);
          }
@@ -832,17 +755,68 @@
      }
    }
    // Notify any change notification listeners that might be registered with
    // the server.
    if (getResultCode() == ResultCode.SUCCESS)
    {
      for (ChangeNotificationListener changeListener :
           DirectoryServer.getChangeNotificationListeners())
      notifyChangeListeners();
    }
    // Stop the processing timer.
    setProcessingStopTime();
  }
  /**
   * Checks to determine whether there has been a request to cancel this
   * operation.  If so, then set the cancel result and processing stop time.
   *
   * @return  {@code true} if there was a cancel request, or {@code false} if
   *          not.
   */
  private boolean cancelIfRequested()
  {
    if (getCancelRequest() == null)
    {
      return false;
    }
    indicateCancelled(getCancelRequest());
    setProcessingStopTime();
    return true;
  }
  /**
   * Gets the entry to modify.
   *
   * @return  The entry retrieved from the backend.
   *
   * @throws  DirectoryException  If a problem occurs while trying to get the
   *                              entry, or if the entry doesn't exist.
   */
  private void getEntryToModify()
          throws DirectoryException
  {
    currentEntry = backend.getEntry(entryDN);
    if (currentEntry == null)
    {
      // See if one of the entry's ancestors exists.
      DN matchedDN = null;
      DN parentDN = entryDN.getParentDNInSuffix();
      while (parentDN != null)
      {
        try
        {
          changeListener.handleModifyOperation(this, currentEntry,
                                               modifiedEntry);
          if (DirectoryServer.entryExists(parentDN))
          {
            matchedDN = parentDN;
            break;
          }
        }
        catch (Exception e)
        {
@@ -850,18 +824,16 @@
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
          Message message = ERR_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER.get(
              getExceptionMessage(e));
          logError(message);
          break;
        }
        parentDN = parentDN.getParentDNInSuffix();
      }
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
                     ERR_MODIFY_NO_SUCH_ENTRY.get(String.valueOf(entryDN)),
                     matchedDN, null);
    }
    // Stop the processing timer.
    setProcessingStopTime();
  }
@@ -1584,7 +1556,8 @@
  /**
   * Performs the initial schema processing for an add modification.
   * Performs the initial schema processing for an add modification and updates
   * the entry appropriately.
   *
   * @param  attr  The attribute being added.
   *
@@ -1676,7 +1649,8 @@
  /**
   * Performs the initial schema processing for a delete modification.
   * Performs the initial schema processing for a delete modification and
   * updates the entry appropriately.
   *
   * @param  attr  The attribute being deleted.
   *
@@ -1736,7 +1710,8 @@
  /**
   * Performs the initial schema processing for a replace modification.
   * Performs the initial schema processing for a replace modification and
   * updates the entry appropriately.
   *
   * @param  attr  The attribute being replaced.
   *
@@ -1897,7 +1872,8 @@
  /**
   * Performs the initial schema processing for an increment modification.
   * Performs the initial schema processing for an increment modification and
   * updates the entry appropriately.
   *
   * @param  attr  The attribute being incremented.
   *
@@ -2408,5 +2384,33 @@
      getResponseControls().add(responseControl);
    }
  }
  /**
   * Notify any registered change listeners about this update.
   */
  private void notifyChangeListeners()
  {
    for (ChangeNotificationListener changeListener :
         DirectoryServer.getChangeNotificationListeners())
    {
      try
      {
        changeListener.handleModifyOperation(this, currentEntry, modifiedEntry);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Message message = ERR_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER.get(
            getExceptionMessage(e));
        logError(message);
      }
    }
  }
}
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -191,7 +191,7 @@
      }
      // Check for a request to cancel this operation.
      if (getCancelRequest() != null)
      if (cancelIfRequested())
      {
        return;
      }
@@ -222,7 +222,7 @@
      // Check for a request to cancel this operation.
      if (getCancelRequest() != null)
      if (cancelIfRequested())
      {
        return;
      }
@@ -328,7 +328,7 @@
    // Check for a request to cancel this operation.
    if (getCancelRequest() != null)
    if (cancelIfRequested())
    {
      return;
    }
@@ -352,6 +352,27 @@
  /**
   * Checks to determine whether there has been a request to cancel this
   * operation.  If so, then set the cancel result and processing stop time.
   *
   * @return  {@code true} if there was a cancel request, or {@code false} if
   *          not.
   */
  private boolean cancelIfRequested()
  {
    if (getCancelRequest() == null)
    {
      return false;
    }
    indicateCancelled(getCancelRequest());
    setProcessingStopTime();
    return true;
  }
  /**
   * Handles any controls contained in the request.
   *
   * @throws  DirectoryException  If there is a problem with any of the request