From 30d5e7846b67d3432b485631f80c71be391868fb Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Tue, 25 Sep 2007 01:54:38 +0000
Subject: [PATCH] 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.

---
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java      |   31 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java   |  232 +++---
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java   |   27 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java |   31 
 opendj-sdk/opends/src/server/org/opends/server/types/Entry.java                                                |  286 +++++--
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java  |   29 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java   |   27 
 opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java                                         | 1574 +++++++++++++++++++++++-----------------
 8 files changed, 1,358 insertions(+), 879 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
index 5e0fc17..461143e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
+++ b/opendj-sdk/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;
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java b/opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java
index da2a3ca..e610a09 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java
+++ b/opendj-sdk/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.
    *
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index 8207729..011d83a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opendj-sdk/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.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
index 206ac65..b69914b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
+++ b/opendj-sdk/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
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
index 02e2887..ee4ddc1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
+++ b/opendj-sdk/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
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
index 8e4ec8d..a7b19b8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
+++ b/opendj-sdk/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
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
index 4caefd9..d38461b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
+++ b/opendj-sdk/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);
+      }
+    }
+  }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
index 15b5543..6ee6d0a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
+++ b/opendj-sdk/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

--
Gitblit v1.10.0