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

Nicolas Capponi
03.53.2014 d45f3ef4848972f83705fede48872b8d91d68349
Checkpoint commit for OPENDJ-1308 Migrate schema support

Align matching rules to SDK

* Remove MatchingRule#normalizeAssertionValue() method
* Remove hashCode() and equals() method in
SubstringMatchingRule$DefaultSubstringAssertion class
* Add DefaultAssertion class in AbstractMatchingRule for
default assertion implementation
* Add specific implementations of getAssertion() method
in ApproximateMatchingRule and EqualityMatchingRule classes
* Add createIndexQuery() method implementation in assertions
returned by AbstractOrderingMatchingRule class

* In SearchFilter class :
** Remove normalization of values for equals()
and hasCode() methods

* In AttributeIndex class :
** Refactor all evaluateXXX methods to use Assertion and IndexQuery
to retrieve entries

* In IndexFilter class :
** Refactor evaluation of bounded range to remove code duplication
(Note that this implementation must be optimized)

* In IndexQuery class :
** Use LocalizableMessageBuilder instead of a List<LocalizableMessage>

* In IndexQueryFactoryImpl class :
** Handle null case for an index in evaluate() methods
** Use presence index if available in query returned by
createMatchAllQuery() method
15 files modified
1715 ■■■■■ changed files
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/AbstractMatchingRule.java 68 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/ApproximateMatchingRule.java 28 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/EqualityMatchingRule.java 41 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/MatchingRule.java 29 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/SubstringMatchingRule.java 66 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java 855 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/IndexFilter.java 109 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/IndexQuery.java 23 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java 94 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/AbstractOrderingMatchingRule.java 53 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CertificateExactMatchingRule.java 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java 131 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java 14 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java 184 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java 11 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/AbstractMatchingRule.java
@@ -52,6 +52,65 @@
public abstract class AbstractMatchingRule implements MatchingRule
{
  /**
   * Default implementation of assertion.
   */
  public static final class DefaultAssertion implements Assertion
  {
    /** The ID of the DB index to use with this assertion. */
    private final String indexID;
    private final ByteSequence normalizedAssertionValue;
    /**
     * Returns the equality assertion.
     *
     * @param normalizedAssertionValue
     *          The value on which the assertion is built.
     * @return the equality assertion
     */
    public static DefaultAssertion equality(final ByteSequence normalizedAssertionValue)
    {
      return new DefaultAssertion("equality", normalizedAssertionValue);
    }
    /**
     * Returns the approximate assertion.
     *
     * @param normalizedAssertionValue
     *          The value on which the assertion is built.
     * @return the approximate assertion
     */
    static DefaultAssertion approximate(final ByteSequence normalizedAssertionValue)
    {
      return new DefaultAssertion("approximate", normalizedAssertionValue);
    }
    private DefaultAssertion(final String indexID, final ByteSequence normalizedAssertionValue)
    {
      this.indexID = indexID;
      this.normalizedAssertionValue = normalizedAssertionValue;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ConditionResult matches(final ByteSequence normalizedAttributeValue)
    {
      return ConditionResult.valueOf(normalizedAssertionValue.equals(normalizedAttributeValue));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(IndexQueryFactory<T> factory)
        throws DecodeException
    {
      return factory.createExactMatchQuery(indexID, normalizedAssertionValue);
    }
  }
  private static final Assertion UNDEFINED_ASSERTION = new Assertion()
  {
    @Override
@@ -71,9 +130,14 @@
  };
  /**
   * {@inheritDoc}
   * Returns the normalized form of the assertion value.
   *
   * @param value
   *            The assertion value to normalize.
   * @return the normalized value
   * @throws DecodeException
   *            If a problem occurs.
   */
  @Override
  public ByteString normalizeAssertionValue(ByteSequence value)
      throws DecodeException
  {
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/ApproximateMatchingRule.java
@@ -26,8 +26,12 @@
 */
package org.opends.server.api;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
/**
 * This class defines the set of methods and structures that must be
@@ -86,5 +90,29 @@
    return ConditionResult.valueOf(
        approximatelyMatch(attributeValue, assertionValue));
  }
  /** {@inheritDoc} */
  @Override
  public Assertion getAssertion(ByteSequence assertionValue) throws DecodeException
  {
    final ByteString normAssertionValue = normalizeAttributeValue(assertionValue);
    final DefaultAssertion approxAssertion = DefaultAssertion.approximate(normAssertionValue);
    return new Assertion()
    {
      @Override
      public ConditionResult matches(ByteSequence normalizedAttributeValue)
      {
        return valuesMatch(normalizedAttributeValue, normAssertionValue);
      }
      @Override
      public <T> T createIndexQuery(IndexQueryFactory<T> factory)
          throws DecodeException
      {
       return approxAssertion.createIndexQuery(factory);
      }
    };
  }
}
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/EqualityMatchingRule.java
@@ -26,8 +26,13 @@
 */
package org.opends.server.api;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.opends.server.api.AbstractMatchingRule.DefaultAssertion;
/**
 * This class defines the set of methods and structures that must be
@@ -108,5 +113,41 @@
  {
    return attributeValue.hashCode();
  }
  /** {@inheritDoc} */
  @Override
  public Assertion getAssertion(ByteSequence assertionValue) throws DecodeException
  {
    final ByteString normAssertionValue = normalizeAttributeValue(assertionValue);
    return getEqualityAssertion(normAssertionValue);
  }
  /**
   * Return the equality assertion for the matching rule.
   *
   * @param normAssertionValue
   *            The normalized assertion value.
   * @return the assertion
   */
  protected Assertion getEqualityAssertion(final ByteString normAssertionValue)
  {
    final DefaultAssertion eqAssertion = DefaultAssertion.equality(normAssertionValue);
    return new Assertion()
    {
      @Override
      public ConditionResult matches(ByteSequence normalizedAttributeValue)
      {
        return valuesMatch(normalizedAttributeValue, normAssertionValue);
      }
      @Override
      public <T> T createIndexQuery(IndexQueryFactory<T> factory)
          throws DecodeException
      {
       return eqAssertion.createIndexQuery(factory);
      }
    };
  }
}
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/MatchingRule.java
@@ -64,25 +64,6 @@
   */
  String getOID();
  /**
   * Retrieves the normalized form of the provided assertion value,
   * which is best suite for efficiently performing matching
   * operations on that value.
   *
   * @param value
   *          The assertion value to be normalized.
   * @return The normalized version of the provided value.
   * @throws DecodeException
   *           If the provided value is invalid according to the
   *           associated attribute syntax.
   */
  ByteString normalizeAssertionValue(ByteSequence value)
      throws DecodeException;
  /**
   * Retrieves the name or OID for this matching rule. If it has a
   * name, then it will be returned. Otherwise, the OID will be
@@ -118,7 +99,7 @@
   * @throws DecodeException
   *           if problem
   */
  Assertion getAssertion(final ByteSequence assertionValue) throws DecodeException;
  Assertion getAssertion(ByteSequence assertionValue) throws DecodeException;
  /**
   * Returns the normalized form of the provided assertion value, which is
@@ -132,7 +113,7 @@
   * @throws DecodeException
   *             if the syntax of the value is not valid.
   */
  public Assertion getGreaterOrEqualAssertion(final ByteSequence assertionValue) throws DecodeException;
  Assertion getGreaterOrEqualAssertion(ByteSequence assertionValue) throws DecodeException;
  /**
   * Returns the normalized form of the provided assertion value, which is
@@ -146,7 +127,7 @@
   * @throws DecodeException
   *             if the syntax of the value is not valid.
   */
  public Assertion getLessOrEqualAssertion(final ByteSequence assertionValue) throws DecodeException;
  Assertion getLessOrEqualAssertion(ByteSequence assertionValue) throws DecodeException;
  /**
   * Returns the normalized form of the provided assertion substring values,
@@ -166,8 +147,8 @@
   * @throws DecodeException
   *             if the syntax of the value is not valid.
   */
  public Assertion getSubstringAssertion(final ByteSequence subInitial,
      final List<? extends ByteSequence> subAnyElements, final ByteSequence subFinal) throws DecodeException;
  Assertion getSubstringAssertion(ByteSequence subInitial, List<? extends ByteSequence> subAnyElements,
      ByteSequence subFinal) throws DecodeException;
  /**
   * Indicates whether this matching rule is declared "OBSOLETE". The
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/api/SubstringMatchingRule.java
@@ -345,72 +345,6 @@
          }
      }
      // TODO : reminder : should add this method in the SDK
      /** {@inheritDoc} */
      @Override
    public int hashCode()
    {
      int hashCode = 0;
      if (normInitial != null)
      {
        hashCode += normInitial.hashCode();
      }
      if (normAnys != null)
      {
        for (ByteString any : normAnys)
        {
          hashCode += any.hashCode();
        }
      }
      if (normFinal != null)
      {
        hashCode += normFinal.hashCode();
      }
      return hashCode;
    }
      // TODO : reminder : should add this method in the SDK
      /** {@inheritDoc} */
      @Override
      public boolean equals(Object obj)
      {
        if (obj == this)
        {
          return true;
        }
        if (! (obj instanceof DefaultSubstringAssertion))
        {
          return false;
        }
        DefaultSubstringAssertion other = (DefaultSubstringAssertion) obj;
        boolean initialCheck = normInitial == null ? other.normInitial == null : normInitial.equals(other.normInitial);
        if (!initialCheck)
        {
          return false;
        }
        boolean finalCheck = normFinal == null ? other.normFinal == null : normFinal.equals(other.normFinal);
        if (!finalCheck)
        {
          return false;
        }
        boolean anyCheck = normAnys == null ? other.normAnys == null : normAnys.length == other.normAnys.length;
        if (!anyCheck)
        {
          return false;
        }
        if (normAnys != null)
        {
          for (int i = 0; i < normAnys.length; i++)
          {
            if (! normAnys[i].equals(other.normAnys[i]))
            {
              return false;
            }
          }
        }
        return true;
      }
  }
  /** {@inheritDoc} */
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -31,8 +31,10 @@
import java.util.concurrent.atomic.AtomicBoolean;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ResultCode;
@@ -41,11 +43,11 @@
import org.forgerock.util.Utils;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn.IndexType;
import org.opends.server.admin.std.server.LocalDBIndexCfg;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.monitors.DatabaseEnvironmentMonitor;
import org.opends.server.types.*;
@@ -119,6 +121,11 @@
   */
  Index approximateIndex = null;
  /** The mapping from names to indexes. */
  private Map<String, Index> nameToIndexes;
  private IndexQueryFactory<IndexQuery> indexQueryFactory;
  /**
   * The ExtensibleMatchingRuleIndex instance for ExtensibleMatchingRule
   * indexes.
@@ -131,6 +138,7 @@
  /**
   * Create a new attribute index object.
   *
   * @param entryContainer The entryContainer of this attribute index.
   * @param state The state database to persist index state info.
   * @param env The JE environment handle.
@@ -139,71 +147,73 @@
   * @throws ConfigException if a configuration related error occurs.
   */
  public AttributeIndex(LocalDBIndexCfg indexConfig, State state,
                        Environment env,
                        EntryContainer entryContainer)
                        Environment env, EntryContainer entryContainer)
      throws DatabaseException, ConfigException
  {
    this.entryContainer = entryContainer;
    this.env = env;
    this.indexConfig = indexConfig;
    this.state = state;
    nameToIndexes = new HashMap<String, Index>();
    AttributeType attrType = indexConfig.getAttribute();
    String name =
        entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID();
    String name = entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID();
    final JEIndexConfig config = new JEIndexConfig(indexConfig.getSubstringLength());
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.EQUALITY))
    if (indexConfig.getIndexType().contains(IndexType.EQUALITY))
    {
      this.equalityIndex = buildExtIndex(
          name, attrType, attrType.getEqualityMatchingRule(), new EqualityIndexer(attrType));
      nameToIndexes.put(IndexType.EQUALITY.toString(), equalityIndex);
    }
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.PRESENCE))
    if (indexConfig.getIndexType().contains(IndexType.PRESENCE))
    {
      this.presenceIndex = newIndex(name + ".presence",
          new PresenceIndexer(attrType), indexConfig.getIndexEntryLimit());
      nameToIndexes.put(IndexType.PRESENCE.toString(), presenceIndex);
    }
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.SUBSTRING))
    if (indexConfig.getIndexType().contains(IndexType.SUBSTRING))
    {
      this.substringIndex = buildExtIndex(
          name, attrType, attrType.getSubstringMatchingRule(), new SubstringIndexer(attrType, config));
      //nameToIndexes.put(IndexType.SUBSTRING.toString(), substringIndex);
    }
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.ORDERING))
    if (indexConfig.getIndexType().contains(IndexType.ORDERING))
    {
      this.orderingIndex = buildExtIndex(
          name, attrType, attrType.getOrderingMatchingRule(), new OrderingIndexer(attrType));
      nameToIndexes.put(IndexType.ORDERING.toString(), orderingIndex);
    }
    if (indexConfig.getIndexType().contains(
        LocalDBIndexCfgDefn.IndexType.APPROXIMATE))
    if (indexConfig.getIndexType().contains(IndexType.APPROXIMATE))
    {
      this.approximateIndex = buildExtIndex(
          name, attrType, attrType.getApproximateMatchingRule(), new ApproximateIndexer(attrType));
      nameToIndexes.put(IndexType.APPROXIMATE.toString(), approximateIndex);
    }
    if (indexConfig.getIndexType().contains(
        LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
    indexQueryFactory = new IndexQueryFactoryImpl(nameToIndexes, config);
    if (indexConfig.getIndexType().contains(IndexType.EXTENSIBLE))
    {
      Set<String> extensibleRules =
              indexConfig.getIndexExtensibleMatchingRule();
      Set<String> extensibleRules = indexConfig.getIndexExtensibleMatchingRule();
      if(extensibleRules == null || extensibleRules.isEmpty())
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, "extensible"));
      }
      extensibleIndexes = new ExtensibleMatchingRuleIndex();
      //Iterate through the Set and create the index only if necessary.
      //Collation equality and Ordering matching rules share the same
      //indexer and index. A Collation substring matching rule is treated
      // differently as it uses a separate indexer and index.
      // Iterate through the Set and create the index only if necessary.
      // Collation equality and Ordering matching rules share the same
      // indexer and index.
      // A Collation substring matching rule is treated differently
      // as it uses a separate indexer and index.
      for(String ruleName:extensibleRules)
      {
        ExtensibleMatchingRule rule =
            DirectoryServer.getExtensibleMatchingRule(toLowerCase(ruleName));
        ExtensibleMatchingRule rule = DirectoryServer.getExtensibleMatchingRule(toLowerCase(ruleName));
        if(rule == null)
        {
          logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
@@ -221,11 +231,9 @@
            extensibleIndexes.addIndex(extIndex, indexID);
          }
          extensibleIndexes.addRule(indexID, rule);
          indexMap.put(indexer.getExtensibleIndexID(),
              extensibleIndexes.getIndex(indexID));
          indexMap.put(indexer.getExtensibleIndexID(), extensibleIndexes.getIndex(indexID));
        }
        IndexQueryFactory<IndexQuery> factory =
            new IndexQueryFactoryImpl(indexMap, config);
        IndexQueryFactory<IndexQuery> factory = new IndexQueryFactoryImpl(indexMap, config);
        extensibleIndexes.addQueryFactory(rule, factory);
      }
    }
@@ -637,127 +645,47 @@
  }
  /**
   * Retrieve the entry IDs that might contain a given substring.
   * @param bytes A normalized substring of an attribute value.
   * @return The candidate entry IDs.
   * Retrieve the entry IDs that might match the provided assertion.
   *
   * @param indexQuery
   *            The query used to retrieve entries.
   * @param indexName
   *            The name of index used to retrieve entries.
   * @param filter
   *          The filter on entries.
   * @param debugBuffer
   *          If not null, a diagnostic string will be written which will help
   *          determine how the indexes contributed to this search.
   * @param monitor
   *          The database environment monitor provider that will keep index
   *          filter usage statistics.
   * @return The candidate entry IDs that might contain the filter assertion
   *         value.
   */
  private EntryIDSet matchSubstring(byte[] bytes)
  { // FIXME replace this code with SDK's
    // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.createIndexQuery()
  private EntryIDSet evaluateIndexQuery(IndexQuery indexQuery, String indexName, SearchFilter filter,
      StringBuilder debugBuffer, DatabaseEnvironmentMonitor monitor)
  {
    LocalizableMessageBuilder debugMessage = monitor.isFilterUseEnabled() ? new LocalizableMessageBuilder() : null;
    EntryIDSet results = indexQuery.evaluate(debugMessage);
    int substrLength = indexConfig.getSubstringLength();
    // There are two cases, depending on whether the user-provided
    // substring is smaller than the configured index substring length or not.
    if (bytes.length < substrLength)
    if (debugBuffer != null)
    {
      // Iterate through all the keys that have this value as the prefix.
      // Set the lower bound for a range search.
      byte[] lower = makeSubstringKey(bytes, 0, bytes.length);
      // Set the upper bound for a range search.
      // We need a key for the upper bound that is of equal length
      // but slightly greater than the lower bound.
      byte[] upper = makeSubstringKey(bytes, 0, bytes.length);
      for (int i = upper.length-1; i >= 0; i--)
      {
        if (upper[i] == 0xFF)
        {
          // We have to carry the overflow to the more significant byte.
          upper[i] = 0;
        }
        else
        {
          // No overflow, we can stop.
          upper[i] = (byte) (upper[i] + 1);
          break;
        }
      }
      // Read the range: lower <= keys < upper.
      return substringIndex.readRange(lower, upper, true, false);
      debugBuffer.append("[INDEX:").append(indexConfig.getAttribute().getNameOrOID())
        .append(".").append(indexName).append("]");
    }
    else
    if (monitor.isFilterUseEnabled())
    {
      // Break the value up into fragments of length equal to the
      // index substring length, and read those keys.
      // Eliminate duplicates by putting the keys into a set.
      Set<byte[]> set =
          new TreeSet<byte[]>(substringIndex.indexer.getComparator());
      // Example: The value is ABCDE and the substring length is 3.
      // We produce the keys ABC BCD CDE.
      for (int first = 0, last = substrLength;
           last <= bytes.length; first++, last++)
      if (results.isDefined())
      {
        set.add(makeSubstringKey(bytes, first, substrLength));
      }
      EntryIDSet results = new EntryIDSet();
      DatabaseEntry key = new DatabaseEntry();
      for (byte[] keyBytes : set)
      {
        // Read the key.
        key.setData(keyBytes);
        EntryIDSet list = substringIndex.readKey(key, null, LockMode.DEFAULT);
        // Incorporate them into the results.
        results.retainAll(list);
        // We may have reached the point of diminishing returns where
        // it is quicker to stop now and process the current small number of
        // candidates.
        if (results.isDefined() &&
             results.size() <= IndexFilter.FILTER_CANDIDATE_THRESHOLD)
        {
          break;
        }
      }
      return results;
    }
  }
  /**
   * Uses an equality index to retrieve the entry IDs that might contain a
   * given initial substring.
   * @param bytes A normalized initial substring of an attribute value.
   * @return The candidate entry IDs.
   */
  private EntryIDSet matchInitialSubstring(byte[] bytes)
  { // FIXME replace this code with SDK's
    // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.createIndexQuery()
    // Iterate through all the keys that have this value as the prefix.
    // Set the lower bound for a range search.
    byte[] lower = bytes;
    // Set the upper bound for a range search.
    // We need a key for the upper bound that is of equal length
    // but slightly greater than the lower bound.
    byte[] upper = new byte[bytes.length];
    System.arraycopy(bytes,0, upper, 0, bytes.length);
    for (int i = upper.length-1; i >= 0; i--)
    {
      if (upper[i] == 0xFF)
      {
        // We have to carry the overflow to the more significant byte.
        upper[i] = 0;
        monitor.updateStats(filter, results.size());
      }
      else
      {
        // No overflow, we can stop.
        upper[i] = (byte) (upper[i] + 1);
        break;
        monitor.updateStats(filter, debugMessage.toMessage());
      }
    }
    // Read the range: lower <= keys < upper.
    return equalityIndex.readRange(lower, upper, true, false);
    return results;
  }
  /**
@@ -776,62 +704,11 @@
                                           StringBuilder debugBuffer,
                                           DatabaseEnvironmentMonitor monitor)
  {
    if (equalityIndex == null)
    {
      if(monitor.isFilterUseEnabled())
      {
        monitor.updateStats(equalityFilter,
            INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("equality",
                indexConfig.getAttribute().getNameOrOID()));
      }
      return new EntryIDSet();
    }
    try
    {
      // Make a key from the normalized assertion value.
      MatchingRule equalityRule = equalityFilter.getAttributeType().
        getEqualityMatchingRule();
      byte[] keyBytes = equalityRule.normalizeAssertionValue(
          equalityFilter.getAssertionValue()).toByteArray();
      DatabaseEntry key = new DatabaseEntry(keyBytes);
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
        debugBuffer.append(".");
        debugBuffer.append("equality]");
      }
      // Read the key.
      EntryIDSet idSet = equalityIndex.readKey(key, null, LockMode.DEFAULT);
      if(monitor.isFilterUseEnabled())
      {
        if(idSet.isDefined())
        {
          monitor.updateStats(equalityFilter, idSet.size());
        }
        else if(!equalityIndex.isTrusted())
        {
          monitor.updateStats(equalityFilter,
              INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                  equalityIndex.getName()));
        }
        else if(equalityIndex.isRebuildRunning())
        {
          monitor.updateStats(equalityFilter,
              INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                  equalityIndex.getName()));
        }
        else
        {
          monitor.updateStats(equalityFilter,
              INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                  equalityIndex.getName()));
        }
      }
      return idSet;
    try {
      final MatchingRule matchRule = equalityFilter.getAttributeType().getEqualityMatchingRule();
      final IndexQuery indexQuery = matchRule.getAssertion(equalityFilter.getAssertionValue())
          .createIndexQuery(indexQueryFactory);
      return evaluateIndexQuery(indexQuery, "equality", equalityFilter, debugBuffer, monitor);
    }
    catch (DecodeException e)
    {
@@ -856,54 +733,8 @@
                                           StringBuilder debugBuffer,
                                           DatabaseEnvironmentMonitor monitor)
  {
    if (presenceIndex == null)
    {
      if(monitor.isFilterUseEnabled())
      {
        monitor.updateStats(filter,
            INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("presence",
                indexConfig.getAttribute().getNameOrOID()));
      }
      return new EntryIDSet();
    }
    if(debugBuffer != null)
    {
      debugBuffer.append("[INDEX:");
      debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
      debugBuffer.append(".");
      debugBuffer.append("presence]");
    }
    // Read the presence key
    EntryIDSet idSet =
        presenceIndex.readKey(presenceKey, null, LockMode.DEFAULT);
    if(monitor.isFilterUseEnabled())
    {
      if(idSet.isDefined())
      {
        monitor.updateStats(filter, idSet.size());
      }
      else if(!presenceIndex.isTrusted())
      {
        monitor.updateStats(filter,
            INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                  presenceIndex.getName()));
      }
      else if(presenceIndex.isRebuildRunning())
      {
        monitor.updateStats(filter,
            INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                  presenceIndex.getName()));
      }
      else
      {
        monitor.updateStats(filter,
            INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                  presenceIndex.getName()));
      }
    }
    return idSet;
    final IndexQuery indexQuery = indexQueryFactory.createMatchAllQuery();
    return evaluateIndexQuery(indexQuery, "presence", filter, debugBuffer, monitor);
  }
  /**
@@ -938,88 +769,22 @@
   * @return The candidate entry IDs that might contain a value
   *         less than or equal to the filter assertion value.
   */
  public EntryIDSet evaluateLessOrEqualFilter(SearchFilter filter,
                                              StringBuilder debugBuffer,
                                             DatabaseEnvironmentMonitor monitor)
  public EntryIDSet evaluateLessOrEqualFilter(SearchFilter filter, StringBuilder debugBuffer,
      DatabaseEnvironmentMonitor monitor)
  {
    return evaluateOrderingFilter(filter, false, debugBuffer, monitor);
  }
  private EntryIDSet evaluateOrderingFilter(SearchFilter filter,
      boolean greater, StringBuilder debugBuffer,
  private EntryIDSet evaluateOrderingFilter(SearchFilter filter, boolean greater, StringBuilder debugBuffer,
      DatabaseEnvironmentMonitor monitor)
  {
    if (orderingIndex == null)
    {
      if(monitor.isFilterUseEnabled())
      {
        monitor.updateStats(filter,
            INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("ordering",
                indexConfig.getAttribute().getNameOrOID()));
      }
      return new EntryIDSet();
    }
    try
    {
      // Use the ordering matching rule to normalize the value.
      MatchingRule orderingRule = filter.getAttributeType().getOrderingMatchingRule();
      byte[] normalizedValue = orderingRule.normalizeAssertionValue(
          filter.getAssertionValue()).toByteArray();
      // Set the lower and upper bounds for a range search.
      byte[] lower;
      byte[] upper;
      if (greater)
      {
        // Set upper bound to 0 to search all keys greater than the lower bound.
        lower = normalizedValue;
        upper = new byte[0];
      }
      else
      {
        // Set lower bound to 0 to start the range search from the smallest key.
        lower = new byte[0];
        upper = normalizedValue;
      }
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
        debugBuffer.append(".");
        debugBuffer.append("ordering]");
      }
      // Read the range: lower <= keys < upper OR lower < keys <= upper
      EntryIDSet idSet = orderingIndex.readRange(lower, upper, greater, !greater);
      if(monitor.isFilterUseEnabled())
      {
        if(idSet.isDefined())
        {
          monitor.updateStats(filter, idSet.size());
        }
        else if(!orderingIndex.isTrusted())
        {
          monitor.updateStats(filter,
              INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                  orderingIndex.getName()));
        }
        else if(orderingIndex.isRebuildRunning())
        {
          monitor.updateStats(filter,
              INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                  orderingIndex.getName()));
        }
        else
        {
          monitor.updateStats(filter,
              INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                  orderingIndex.getName()));
        }
      }
      return idSet;
    try {
      final MatchingRule matchRule = filter.getAttributeType().getOrderingMatchingRule();
      final Assertion assertion = greater ?
          matchRule.getGreaterOrEqualAssertion(filter.getAssertionValue()) :
          matchRule.getLessOrEqualAssertion(filter.getAssertionValue());
      final IndexQuery indexQuery = assertion.createIndexQuery(indexQueryFactory);
      return evaluateIndexQuery(indexQuery, "ordering", filter, debugBuffer, monitor);
    }
    catch (DecodeException e)
    {
@@ -1044,165 +809,12 @@
                                            StringBuilder debugBuffer,
                                            DatabaseEnvironmentMonitor monitor)
  {
    SubstringMatchingRule matchRule =
         filter.getAttributeType().getSubstringMatchingRule();
    try
    {
      ArrayList<ByteString> elements = new ArrayList<ByteString>();
      EntryIDSet results = new EntryIDSet();
      if (filter.getSubInitialElement() != null)
      {
        // Use the equality index for initial substrings if possible.
        if (equalityIndex != null && matchRule != null)
        {
          ByteString normValue =
               matchRule.normalizeSubstring(filter.getSubInitialElement());
          byte[] normBytes = normValue.toByteArray();
          EntryIDSet list = matchInitialSubstring(normBytes);
          results.retainAll(list);
          if (results.isDefined() &&
               results.size() <= IndexFilter.FILTER_CANDIDATE_THRESHOLD)
          {
            if(debugBuffer != null)
            {
              debugBuffer.append("[INDEX:");
              debugBuffer.append(indexConfig.getAttribute().
                  getNameOrOID());
              debugBuffer.append(".");
              debugBuffer.append("equality]");
            }
            if(monitor.isFilterUseEnabled())
            {
              monitor.updateStats(filter, results.size());
            }
            return results;
          }
        }
        else
        {
          elements.add(filter.getSubInitialElement());
        }
      }
      if (substringIndex == null)
      {
        if(monitor.isFilterUseEnabled())
        {
          if(!results.isDefined())
          {
            if(filter.getSubInitialElement() != null)
            {
              if(equalityIndex == null)
              {
                monitor.updateStats(filter,
                    INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("equality",
                        indexConfig.getAttribute().getNameOrOID()));
              }
              else if(!equalityIndex.isTrusted())
              {
                monitor.updateStats(filter,
                    INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                        equalityIndex.getName()));
              }
              else if(equalityIndex.isRebuildRunning())
              {
                monitor.updateStats(filter,
                    INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                        equalityIndex.getName()));
              }
              else
              {
                monitor.updateStats(filter,
                    INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                        equalityIndex.getName()));
              }
            }
            else
            {
              monitor.updateStats(filter,
                  INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("substring",
                      indexConfig.getAttribute().getNameOrOID()));
            }
          }
          else
          {
            monitor.updateStats(filter, results.size());
          }
        }
        return results;
      }
      // We do not distinguish between sub and final elements
      // in the substring index. Put all the elements into a single list.
      elements.addAll(filter.getSubAnyElements());
      if (filter.getSubFinalElement() != null)
      {
        elements.add(filter.getSubFinalElement());
      }
      // Iterate through each substring element.
      for (ByteString element : elements)
      {
        // Normalize the substring according to the substring matching rule.
        ByteString normValue = matchRule.normalizeSubstring(element);
        byte[] normBytes = normValue.toByteArray();
        // Get the candidate entry IDs from the index.
        EntryIDSet list = matchSubstring(normBytes);
        // Incorporate them into the results.
        results.retainAll(list);
        // We may have reached the point of diminishing returns where
        // it is quicker to stop now and process the current small number of
        // candidates.
        if (results.isDefined() &&
             results.size() <= IndexFilter.FILTER_CANDIDATE_THRESHOLD)
        {
          break;
        }
      }
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
        debugBuffer.append(".");
        debugBuffer.append("substring]");
      }
      if(monitor.isFilterUseEnabled())
      {
        if(results.isDefined())
        {
          monitor.updateStats(filter, results.size());
        }
        else if(!substringIndex.isTrusted())
        {
          monitor.updateStats(filter,
              INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                  substringIndex.getName()));
        }
        else if(substringIndex.isRebuildRunning())
        {
          monitor.updateStats(filter,
              INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                  substringIndex.getName()));
        }
        else
        {
          monitor.updateStats(filter,
              INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                  substringIndex.getName()));
        }
      }
      return results;
    try {
      final MatchingRule matchRule = filter.getAttributeType().getSubstringMatchingRule();
      final IndexQuery indexQuery = matchRule.getSubstringAssertion(
          filter.getSubInitialElement(), filter.getSubAnyElements(), filter.getSubFinalElement())
          .createIndexQuery(indexQueryFactory);
      return evaluateIndexQuery(indexQuery, "substring", filter, debugBuffer, monitor);
    }
    catch (DecodeException e)
    {
@@ -1212,36 +824,41 @@
  }
  /**
   * Retrieve the entry IDs that might have a value greater than or
   * equal to the lower bound value, and less than or equal to the
   * upper bound value.
   * Retrieve the entry IDs that might match two filters that restrict a value
   * to both a lower bound and an upper bound.
   *
   * @param lowerValue The lower bound assertion value
   * @param upperValue The upper bound assertion value
   * @return The candidate entry IDs.
   * @param filter1
   *          The first filter, that is either a less-or-equal filter or a
   *          greater-or-equal filter.
   * @param filter2
   *          The second filter, that is either a less-or-equal filter or a
   *          greater-or-equal filter. It must not be of the same type than the
   *          first filter.
   * @param debugBuffer
   *          If not null, a diagnostic string will be written which will help
   *          determine how the indexes contributed to this search.
   * @param monitor
   *          The database environment monitor provider that will keep index
   *          filter usage statistics.
   * @return The candidate entry IDs that might contain match both filters.
   */
  public EntryIDSet evaluateBoundedRange(ByteString lowerValue, ByteString upperValue)
  public EntryIDSet evaluateBoundedRange(SearchFilter filter1, SearchFilter filter2, StringBuilder debugBuffer,
      DatabaseEnvironmentMonitor monitor)
  {
    if (orderingIndex == null)
    {
      return new EntryIDSet();
    }
    try
    {
      // Set the lower and upper bounds for a range search.
      MatchingRule orderingRule = getAttributeType().getOrderingMatchingRule();
      byte[] lower = orderingRule.normalizeAssertionValue(lowerValue).toByteArray();
      byte[] upper = orderingRule.normalizeAssertionValue(upperValue).toByteArray();
      // Read the range: lower <= keys <= upper.
      return orderingIndex.readRange(lower, upper, true, true);
    }
    catch (DecodeException e)
    {
      logger.traceException(e);
      return new EntryIDSet();
    }
    // TODO : this implementation is not optimal
    // as it implies two separate evaluations instead of a single one,
    // thus defeating the purpose of the optimisation done
    // in IndexFilter#evaluateLogicalAndFilter method.
    // One solution could be to implement a boundedRangeAssertion that combine
    // the two operations in one.
    EntryIDSet results = filter1.getFilterType() == FilterType.LESS_OR_EQUAL ?
        evaluateLessOrEqualFilter(filter1, debugBuffer, monitor) :
        evaluateGreaterOrEqualFilter(filter1, debugBuffer, monitor);
    EntryIDSet results2 = filter2.getFilterType() == FilterType.LESS_OR_EQUAL ?
        evaluateLessOrEqualFilter(filter2, debugBuffer, monitor) :
        evaluateGreaterOrEqualFilter(filter2, debugBuffer, monitor);
    results.retainAll(results2);
    return results;
  }
  /**
@@ -1356,66 +973,14 @@
   * @return The candidate entry IDs that might contain the filter
   *         assertion value.
   */
  public EntryIDSet evaluateApproximateFilter(SearchFilter approximateFilter,
                                              StringBuilder debugBuffer,
                                             DatabaseEnvironmentMonitor monitor)
  public EntryIDSet evaluateApproximateFilter(SearchFilter approximateFilter, StringBuilder debugBuffer,
      DatabaseEnvironmentMonitor monitor)
  {
    if (approximateIndex == null)
    {
      if(monitor.isFilterUseEnabled())
      {
        monitor.updateStats(approximateFilter,
            INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("approximate",
                indexConfig.getAttribute().getNameOrOID()));
      }
      return new EntryIDSet();
    }
    try
    {
      MatchingRule approximateMatchingRule =
          approximateFilter.getAttributeType().getApproximateMatchingRule();
      // Make a key from the normalized assertion value.
      byte[] keyBytes = approximateMatchingRule.normalizeAssertionValue(
          approximateFilter.getAssertionValue()).toByteArray();
      DatabaseEntry key = new DatabaseEntry(keyBytes);
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
        debugBuffer.append(".");
        debugBuffer.append("approximate]");
      }
      // Read the key.
      EntryIDSet idSet = approximateIndex.readKey(key, null, LockMode.DEFAULT);
      if(monitor.isFilterUseEnabled())
      {
        if(idSet.isDefined())
        {
          monitor.updateStats(approximateFilter, idSet.size());
        }
        else if(!approximateIndex.isTrusted())
        {
          monitor.updateStats(approximateFilter,
              INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                  approximateIndex.getName()));
        }
        else if(approximateIndex.isRebuildRunning())
        {
          monitor.updateStats(approximateFilter,
              INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                  approximateIndex.getName()));
        }
        else
        {
          monitor.updateStats(approximateFilter,
              INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                  approximateIndex.getName()));
        }
      }
      return idSet;
    try {
      MatchingRule matchRule = approximateFilter.getAttributeType().getApproximateMatchingRule();
      IndexQuery indexQuery = matchRule.getAssertion(approximateFilter.getAssertionValue())
          .createIndexQuery(indexQueryFactory);
      return evaluateIndexQuery(indexQuery, "approximate", approximateFilter, debugBuffer, monitor);
    }
    catch (DecodeException e)
    {
@@ -1609,8 +1174,7 @@
    try
    {
      AttributeType attrType = cfg.getAttribute();
      String name =
        entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID();
      String name = entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID();
      final int indexEntryLimit = cfg.getIndexEntryLimit();
      final JEIndexConfig config = new JEIndexConfig(cfg.getSubstringLength());
@@ -1620,6 +1184,7 @@
        {
          equalityIndex = openNewIndex(name, attrType,
              new EqualityIndexer(attrType), adminActionRequired, messages);
          nameToIndexes.put(LocalDBIndexCfgDefn.IndexType.EQUALITY.toString(), equalityIndex);
        }
        else
        {
@@ -1627,8 +1192,7 @@
          if(this.equalityIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD
                .get(equalityIndex.getName()));
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(equalityIndex.getName()));
            this.equalityIndex.setIndexEntryLimit(indexEntryLimit);
          }
        }
@@ -1640,6 +1204,7 @@
          entryContainer.exclusiveLock.lock();
          try
          {
            nameToIndexes.remove(LocalDBIndexCfgDefn.IndexType.EQUALITY.toString());
            entryContainer.deleteDatabase(equalityIndex);
            equalityIndex = null;
          }
@@ -1657,6 +1222,7 @@
          Indexer presenceIndexer = new PresenceIndexer(attrType);
          presenceIndex = newIndex(name + ".presence", presenceIndexer, indexEntryLimit);
          openIndex(presenceIndex, adminActionRequired, messages);
          nameToIndexes.put(LocalDBIndexCfgDefn.IndexType.PRESENCE.toString(), presenceIndex);
        }
        else
        {
@@ -1664,8 +1230,7 @@
          if(this.presenceIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD
                .get(presenceIndex.getName()));
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(presenceIndex.getName()));
          }
        }
      }
@@ -1676,6 +1241,7 @@
          entryContainer.exclusiveLock.lock();
          try
          {
            nameToIndexes.remove(LocalDBIndexCfgDefn.IndexType.PRESENCE.toString());
            entryContainer.deleteDatabase(presenceIndex);
            presenceIndex = null;
          }
@@ -1695,6 +1261,7 @@
          Index index = newIndex(name + "." + indexer.getExtensibleIndexID(),
              extIndexer, indexEntryLimit);
          substringIndex = openIndex(index, adminActionRequired, messages);
          nameToIndexes.put(LocalDBIndexCfgDefn.IndexType.SUBSTRING.toString(), substringIndex);
        }
        else
        {
@@ -1702,8 +1269,7 @@
          if(this.substringIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD
                .get(substringIndex.getName()));
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(substringIndex.getName()));
          }
          if (indexConfig.getSubstringLength() != cfg.getSubstringLength())
@@ -1719,6 +1285,7 @@
          entryContainer.exclusiveLock.lock();
          try
          {
            nameToIndexes.remove(LocalDBIndexCfgDefn.IndexType.SUBSTRING.toString());
            entryContainer.deleteDatabase(substringIndex);
            substringIndex = null;
          }
@@ -1735,6 +1302,7 @@
        {
          orderingIndex = openNewIndex(name, attrType,
              new OrderingIndexer(attrType), adminActionRequired, messages);
          nameToIndexes.put(LocalDBIndexCfgDefn.IndexType.ORDERING.toString(), orderingIndex);
        }
        else
        {
@@ -1742,8 +1310,7 @@
          if(this.orderingIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD
                .get(orderingIndex.getName()));
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(orderingIndex.getName()));
          }
        }
      }
@@ -1754,6 +1321,7 @@
          entryContainer.exclusiveLock.lock();
          try
          {
            nameToIndexes.remove(LocalDBIndexCfgDefn.IndexType.ORDERING.toString());
            entryContainer.deleteDatabase(orderingIndex);
            orderingIndex = null;
          }
@@ -1764,13 +1332,13 @@
        }
      }
      if (cfg.getIndexType().contains(
              LocalDBIndexCfgDefn.IndexType.APPROXIMATE))
      if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.APPROXIMATE))
      {
        if(approximateIndex == null)
        {
          approximateIndex = openNewIndex(name, attrType,
              new ApproximateIndexer(attrType), adminActionRequired, messages);
          nameToIndexes.put(LocalDBIndexCfgDefn.IndexType.APPROXIMATE.toString(), approximateIndex);
        }
        else
        {
@@ -1778,8 +1346,7 @@
          if(this.approximateIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD
                .get(approximateIndex.getName()));
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(approximateIndex.getName()));
          }
        }
      }
@@ -1790,6 +1357,7 @@
          entryContainer.exclusiveLock.lock();
          try
          {
            nameToIndexes.remove(LocalDBIndexCfgDefn.IndexType.APPROXIMATE.toString());
            entryContainer.deleteDatabase(approximateIndex);
            approximateIndex = null;
          }
@@ -1800,22 +1368,18 @@
        }
      }
      if (cfg.getIndexType().contains(
              LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
      if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
      {
        Set<String> extensibleRules =
            cfg.getIndexExtensibleMatchingRule();
        Set<ExtensibleMatchingRule> validRules =
                                      new HashSet<ExtensibleMatchingRule>();
        Set<String> extensibleRules = cfg.getIndexExtensibleMatchingRule();
        Set<ExtensibleMatchingRule> validRules = new HashSet<ExtensibleMatchingRule>();
        if(extensibleIndexes == null)
        {
          extensibleIndexes = new ExtensibleMatchingRuleIndex();
        }
        for(String ruleName:extensibleRules)
        {
          ExtensibleMatchingRule rule =
              DirectoryServer.getExtensibleMatchingRule(toLowerCase(ruleName));
           if(rule == null)
          ExtensibleMatchingRule rule = DirectoryServer.getExtensibleMatchingRule(toLowerCase(ruleName));
          if(rule == null)
          {
            logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
            continue;
@@ -1838,8 +1402,7 @@
              if(extensibleIndex.setIndexEntryLimit(indexEntryLimit))
              {
                adminActionRequired.set(true);
                messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD
                    .get(extensibleIndex.getName()));
                messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(extensibleIndex.getName()));
              }
              if (indexConfig.getSubstringLength() != cfg.getSubstringLength())
              {
@@ -1850,8 +1413,7 @@
            extensibleIndexes.addRule(indexID, rule);
            indexMap.put(indexer.getExtensibleIndexID(), extensibleIndexes.getIndex(indexID));
          }
          final IndexQueryFactory<IndexQuery> factory =
              new IndexQueryFactoryImpl(indexMap, config);
          IndexQueryFactory<IndexQuery> factory = new IndexQueryFactoryImpl(indexMap, config);
          extensibleIndexes.addQueryFactory(rule, factory);
        }
        //Some rules might have been removed from the configuration.
@@ -1928,8 +1490,7 @@
      indexConfig = cfg;
      return new ConfigChangeResult(
          ResultCode.SUCCESS, adminActionRequired.get(), messages);
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired.get(), messages);
    }
    catch(Exception e)
    {
@@ -2168,101 +1729,37 @@
                                             DatabaseEnvironmentMonitor monitor)
  {
    //Get the Matching Rule OID of the filter.
    String nOID  = extensibleFilter.getMatchingRuleID();
    String matchRuleOID  = extensibleFilter.getMatchingRuleID();
    /**
     * Use the default equality index in two conditions:
     * 1. There is no matching rule provided
     * 2. The matching rule specified is actually the default equality.
     */
    MatchingRule eqRule =
            indexConfig.getAttribute().getEqualityMatchingRule();
    if (nOID == null
        || nOID.equals(eqRule.getOID())
        || nOID.equalsIgnoreCase(eqRule.getNameOrOID()))
    MatchingRule eqRule = indexConfig.getAttribute().getEqualityMatchingRule();
    if (matchRuleOID == null
        || matchRuleOID.equals(eqRule.getOID())
        || matchRuleOID.equalsIgnoreCase(eqRule.getNameOrOID()))
    {
      //No matching rule is defined; use the default equality matching rule.
      if(equalityIndex == null)
      {
        // There is no index on this matching rule.
        if(monitor.isFilterUseEnabled())
        {
          monitor.updateStats(extensibleFilter,
              INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get("equality",
                  indexConfig.getAttribute().getNameOrOID()));
        }
        return IndexQuery.createNullIndexQuery().evaluate(null);
      }
      try
      {
        // Make a key from the normalized assertion value.
        MatchingRule rule =
            extensibleFilter.getAttributeType().getEqualityMatchingRule();
        ByteString value = extensibleFilter.getAssertionValue();
        byte[] keyBytes = rule.normalizeAssertionValue(value).toByteArray();
        DatabaseEntry key = new DatabaseEntry(keyBytes);
        if(debugBuffer != null)
        {
          debugBuffer.append("[INDEX:");
          debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
          debugBuffer.append(".");
          debugBuffer.append("equality]");
        }
        // Read the key.
        EntryIDSet idSet = equalityIndex.readKey(key, null, LockMode.DEFAULT);
        if(monitor.isFilterUseEnabled())
        {
          if(idSet.isDefined())
          {
            monitor.updateStats(extensibleFilter, idSet.size());
          }
          else if(!equalityIndex.isTrusted())
          {
            monitor.updateStats(extensibleFilter,
                INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                  equalityIndex.getName()));
          }
          else if(equalityIndex.isRebuildRunning())
          {
            monitor.updateStats(extensibleFilter,
                INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                  equalityIndex.getName()));
          }
          else
          {
            monitor.updateStats(extensibleFilter,
                INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                  equalityIndex.getName()));
          }
        }
        return idSet;
      }
      catch (DecodeException e)
      {
        logger.traceException(e);
        return IndexQuery.createNullIndexQuery().evaluate(null);
      }
      return evaluateEqualityFilter(extensibleFilter, debugBuffer, monitor);
    }
    ExtensibleMatchingRule rule =
            DirectoryServer.getExtensibleMatchingRule(nOID);
    ExtensibleMatchingRule rule = DirectoryServer.getExtensibleMatchingRule(matchRuleOID);
    IndexQueryFactory<IndexQuery> factory = null;
    if(extensibleIndexes == null
            || (factory = extensibleIndexes.getQueryFactory(rule))==null)
    if (extensibleIndexes == null || (factory = extensibleIndexes.getQueryFactory(rule)) == null)
    {
      // There is no index on this matching rule.
      if(monitor.isFilterUseEnabled())
      if (monitor.isFilterUseEnabled())
      {
        monitor.updateStats(extensibleFilter,
            INFO_JEB_INDEX_FILTER_MATCHING_RULE_NOT_INDEXED.get(nOID,
                indexConfig.getAttribute().getNameOrOID()));
        monitor.updateStats(extensibleFilter, INFO_JEB_INDEX_FILTER_MATCHING_RULE_NOT_INDEXED.get(
            matchRuleOID, indexConfig.getAttribute().getNameOrOID()));
      }
      return IndexQuery.createNullIndexQuery().evaluate(null);
    }
    try
    {
      if(debugBuffer != null)
      if (debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        for (ExtensibleIndexer indexer : rule.getIndexers())
@@ -2276,25 +1773,17 @@
      }
      ByteString assertionValue = extensibleFilter.getAssertionValue();
      IndexQuery expression = rule.createIndexQuery(assertionValue, factory);
      List<LocalizableMessage> debugMessages =
          monitor.isFilterUseEnabled() ? new ArrayList<LocalizableMessage>() : null;
      EntryIDSet idSet = expression.evaluate(debugMessages);
      if(monitor.isFilterUseEnabled())
      LocalizableMessageBuilder debugMessage = monitor.isFilterUseEnabled() ? new LocalizableMessageBuilder() : null;
      EntryIDSet idSet = expression.evaluate(debugMessage);
      if (monitor.isFilterUseEnabled())
      {
        if(idSet.isDefined())
        if (idSet.isDefined())
        {
          monitor.updateStats(extensibleFilter, idSet.size());
        }
        else
        {
          if(debugMessages != null && !debugMessages.isEmpty())
          {
            monitor.updateStats(extensibleFilter, debugMessages.get(0));
          }
          else
          {
            monitor.updateStats(extensibleFilter, LocalizableMessage.EMPTY);
          }
          monitor.updateStats(extensibleFilter, debugMessage.toMessage());
        }
      }
      return idSet;
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/IndexFilter.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2014 ForgeRock AS
 *
 */
package org.opends.server.backends.jeb;
@@ -298,11 +298,10 @@
      ArrayList<SearchFilter> rangeList = rangeEntry.getValue();
      if (rangeList.size() == 2)
      {
        SearchFilter a = rangeList.get(0);
        SearchFilter b = rangeList.get(1);
        SearchFilter filter1 = rangeList.get(0);
        SearchFilter filter2 = rangeList.get(1);
        AttributeIndex attributeIndex =
             entryContainer.getAttributeIndex(rangeEntry.getKey());
        AttributeIndex attributeIndex = entryContainer.getAttributeIndex(rangeEntry.getKey());
        if (attributeIndex == null)
        {
          if(monitor.isFilterUseEnabled())
@@ -313,104 +312,18 @@
          }
          continue;
        }
        EntryIDSet set = attributeIndex.evaluateBoundedRange(filter1, filter2, buffer, monitor);
        if (a.getFilterType() == FilterType.GREATER_OR_EQUAL &&
             b.getFilterType() == FilterType.LESS_OR_EQUAL)
        if(monitor.isFilterUseEnabled() && set.isDefined())
        {
          // Like (cn>=A)(cn<=B).
          EntryIDSet set;
          set = attributeIndex.evaluateBoundedRange(a.getAssertionValue(),
                                                     b.getAssertionValue());
          if (buffer != null)
          {
            a.toString(buffer);
            b.toString(buffer);
            set.toString(buffer);
          }
          if(monitor.isFilterUseEnabled())
          {
            if(set.isDefined())
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  set.size());
            }
            else if(!attributeIndex.orderingIndex.isTrusted())
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                      attributeIndex.orderingIndex.getName()));
            }
            else if(attributeIndex.orderingIndex.isRebuildRunning())
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                      attributeIndex.orderingIndex.getName()));
            }
            else
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                      attributeIndex.orderingIndex.getName()));
            }
          }
          if (retainAll(results, set))
          {
            return results;
          }
          continue;
          monitor.updateStats(SearchFilter.createANDFilter(rangeList), set.size());
        }
        else if (a.getFilterType() == FilterType.LESS_OR_EQUAL &&
             b.getFilterType() == FilterType.GREATER_OR_EQUAL)
        if (retainAll(results, set))
        {
          // Like (cn<=A)(cn>=B).
          EntryIDSet set;
          set = attributeIndex.evaluateBoundedRange(b.getAssertionValue(),
                                                     a.getAssertionValue());
          if (buffer != null)
          {
            a.toString(buffer);
            b.toString(buffer);
            set.toString(buffer);
          }
          if(monitor.isFilterUseEnabled())
          {
            if(set.isDefined())
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  set.size());
            }
            else if(!attributeIndex.orderingIndex.isTrusted())
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                      attributeIndex.orderingIndex.getName()));
            }
            else if(attributeIndex.orderingIndex.isRebuildRunning())
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                      attributeIndex.orderingIndex.getName()));
            }
            else
            {
              monitor.updateStats(SearchFilter.createANDFilter(rangeList),
                  INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                      attributeIndex.orderingIndex.getName()));
            }
          }
          if (retainAll(results, set))
          {
            return results;
          }
          continue;
          return results;
        }
        continue;
      }
      // Add to the remaining range components to be processed.
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/IndexQuery.java
@@ -29,10 +29,9 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import java.util.Collection;
import java.util.List;
import static org.opends.server.backends.jeb.IndexFilter.*;
@@ -51,12 +50,12 @@
  /**
   * Evaluates the index query and returns the EntryIDSet.
   *
   * @param debugMessages If not null, diagnostic messages will be written
   * @param debugMessage If not null, diagnostic message will be written
   *                      which will help to determine why the returned
   *                      EntryIDSet is not defined.
   * @return The EntryIDSet as a result of evaulation of this query.
   * @return The EntryIDSet as a result of evaluation of this query.
   */
  public abstract EntryIDSet evaluate(List<LocalizableMessage> debugMessages);
  public abstract EntryIDSet evaluate(LocalizableMessageBuilder debugMessage);
@@ -117,7 +116,7 @@
     * @param debugMessages
     */
    @Override
    public EntryIDSet evaluate(List<LocalizableMessage> debugMessages)
    public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
    {
      return new EntryIDSet();
    }
@@ -154,18 +153,18 @@
     * @param debugMessages
     */
    @Override
    public EntryIDSet evaluate(List<LocalizableMessage> debugMessages)
    public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
    {
      EntryIDSet entryIDs = null;
      for (IndexQuery query : subIndexQueries)
      {
        if (entryIDs == null)
        {
          entryIDs = query.evaluate(debugMessages);
          entryIDs = query.evaluate(debugMessage);
        }
        else
        {
          entryIDs.retainAll(query.evaluate(debugMessages));
          entryIDs.retainAll(query.evaluate(debugMessage));
        }
        if (entryIDs.isDefined()
            && entryIDs.size() <= FILTER_CANDIDATE_THRESHOLD)
@@ -207,18 +206,18 @@
     * @param debugMessages
     */
    @Override
    public EntryIDSet evaluate(List<LocalizableMessage> debugMessages)
    public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
    {
      EntryIDSet entryIDs = null;
      for (IndexQuery query : subIndexQueries)
      {
        if (entryIDs == null)
        {
          entryIDs = query.evaluate(debugMessages);
          entryIDs = query.evaluate(debugMessage);
        }
        else
        {
          entryIDs.addAll(query.evaluate(debugMessages));
          entryIDs.addAll(query.evaluate(debugMessage));
        }
        if (entryIDs.isDefined()
            && entryIDs.size() <= FILTER_CANDIDATE_THRESHOLD)
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java
@@ -27,10 +27,9 @@
package org.opends.server.backends.jeb;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
@@ -48,6 +47,8 @@
    IndexQueryFactory<IndexQuery>
{
  private static final String PRESENCE_INDEX_KEY = "presence";
  /**
   * The Map containing the string type identifier and the corresponding index.
   */
@@ -79,34 +80,35 @@
      {
        @Override
        public EntryIDSet evaluate(List<LocalizableMessage> debugMessages)
        public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
        {
          // Read the database and get Record for the key.
          DatabaseEntry key = new DatabaseEntry(value.toByteArray());
          // Select the right index to be used.
          Index index = indexMap.get(indexID);
          EntryIDSet entrySet =
              index.readKey(key, null, LockMode.DEFAULT);
          if(debugMessages != null && !entrySet.isDefined())
          if (index == null)
          {
            if(debugMessage != null)
            {
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get(indexID, ""));
            }
            return createMatchAllQuery().evaluate(debugMessage);
          }
          EntryIDSet entrySet = index.readKey(key, null, LockMode.DEFAULT);
          if(debugMessage != null && !entrySet.isDefined())
          {
            if(!index.isTrusted())
            {
              debugMessages.add(
                  INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                      index.getName()));
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(index.getName()));
            }
            else if(index.isRebuildRunning())
            {
              debugMessages.add(
                  INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                      index.getName()));
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(index.getName()));
            }
            else
            {
              debugMessages.add(
                  INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                      index.getName()));
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(index.getName()));
            }
          }
          return entrySet;
@@ -126,32 +128,33 @@
      {
        @Override
        public EntryIDSet evaluate(List<LocalizableMessage> debugMessages)
        public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
        {
          // Find the right index.
          Index index = indexMap.get(indexID);
          EntryIDSet entrySet =
              index.readRange(lowerBound.toByteArray(), upperBound
                  .toByteArray(), includeLowerBound, includeUpperBound);
          if(debugMessages != null && !entrySet.isDefined())
          if (index == null)
          {
            if(debugMessage != null)
            {
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get(indexID, ""));
            }
            return createMatchAllQuery().evaluate(debugMessage);
          }
          EntryIDSet entrySet = index.readRange(lowerBound.toByteArray(), upperBound.toByteArray(),
              includeLowerBound, includeUpperBound);
          if(debugMessage != null && !entrySet.isDefined())
          {
            if(!index.isTrusted())
            {
              debugMessages.add(
                  INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(
                      index.getName()));
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(index.getName()));
            }
            else if(index.isRebuildRunning())
            {
              debugMessages.add(
                  INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(
                      index.getName()));
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(index.getName()));
            }
            else
            {
              debugMessages.add(
                  INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(
                      index.getName()));
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(index.getName()));
            }
          }
          return entrySet;
@@ -191,11 +194,38 @@
  {
    return new IndexQuery()
      {
        @Override
        public EntryIDSet evaluate(List<LocalizableMessage> debugMessages)
        public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
        {
          return new EntryIDSet();
          Index index = indexMap.get(PRESENCE_INDEX_KEY);
          if (index == null)
          {
            if(debugMessage != null)
            {
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_TYPE_DISABLED.get(index.getName(), ""));
            }
            return new EntryIDSet();
          }
          EntryIDSet entrySet = index.readKey(AttributeIndex.presenceKey, null, LockMode.DEFAULT);
          if (debugMessage != null && !entrySet.isDefined())
          {
            if (!index.isTrusted())
            {
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_NOT_TRUSTED.get(index.getName()));
            }
            else if (index.isRebuildRunning())
            {
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(index.getName()));
            }
            else
            {
              debugMessage.append(INFO_JEB_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(index.getName()));
            }
          }
          return entrySet;
        }
      };
  }
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/AbstractOrderingMatchingRule.java
@@ -28,8 +28,8 @@
import java.util.Comparator;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.opends.server.api.AbstractMatchingRule;
import org.opends.server.api.NotImplementedAssertion;
import org.opends.server.api.OrderingMatchingRule;
/**
@@ -57,46 +57,61 @@
  /** {@inheritDoc} */
  @Override
  public Assertion getAssertion(final ByteSequence value)
      throws DecodeException
  public Assertion getAssertion(final ByteSequence assertionValue) throws DecodeException
  {
    final ByteString assertionValue = normalizeAssertionValue(value);
    return new NotImplementedAssertion()
    final ByteString normAssertionValue = normalizeAttributeValue(assertionValue);
    return new Assertion()
    {
      @Override
      public ConditionResult matches(ByteSequence attributeValue)
      public ConditionResult matches(final ByteSequence attributeValue)
      {
        return ConditionResult.valueOf(compareValues(attributeValue, assertionValue) < 0);
        return ConditionResult.valueOf(compareValues(attributeValue, normAssertionValue) < 0);
      }
      @Override
      public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException
      {
        return factory.createRangeMatchQuery("ordering", ByteString.empty(), normAssertionValue, false, false);
      }
    };
  }
  /** {@inheritDoc} */
  @Override
  public Assertion getGreaterOrEqualAssertion(ByteSequence value) throws DecodeException
  public Assertion getGreaterOrEqualAssertion(final ByteSequence assertionValue) throws DecodeException
  {
    final ByteString normAssertion = normalizeAssertionValue(value);
    return new NotImplementedAssertion()
    final ByteString normAssertionValue = normalizeAttributeValue(assertionValue);
    return new Assertion()
    {
      @Override
      public ConditionResult matches(final ByteSequence normalizedAttributeValue)
      {
        return ConditionResult.valueOf(compareValues(normalizedAttributeValue, normAssertion) >= 0);
      }
        @Override
        public ConditionResult matches(final ByteSequence normalizedAttributeValue) {
          return ConditionResult.valueOf(compareValues(normalizedAttributeValue, normAssertionValue) >= 0);
        }
        @Override
        public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
            return factory.createRangeMatchQuery("ordering", normAssertionValue, ByteString.empty(), true, false);
        }
    };
  }
  /** {@inheritDoc} */
  @Override
  public Assertion getLessOrEqualAssertion(ByteSequence value) throws DecodeException
  public Assertion getLessOrEqualAssertion(final ByteSequence assertionValue) throws DecodeException
  {
    final ByteString normAssertion = normalizeAssertionValue(value);
    return new NotImplementedAssertion()
    final ByteString normAssertionValue = normalizeAttributeValue(assertionValue);
    return new Assertion()
    {
      @Override
      public ConditionResult matches(final ByteSequence normalizedAttributeValue)
      {
        return ConditionResult.valueOf(compareValues(normalizedAttributeValue, normAssertion) <= 0);
        return ConditionResult.valueOf(compareValues(normalizedAttributeValue, normAssertionValue) <= 0);
      }
      @Override
      public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException
      {
        return factory.createRangeMatchQuery("ordering", ByteString.empty(), normAssertionValue, false, true);
      }
    };
  }
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CertificateExactMatchingRule.java
@@ -42,6 +42,7 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
@@ -369,5 +370,13 @@
    return builder.toByteString();
  }
  /** {@inheritDoc} */
  @Override
  public Assertion getAssertion(ByteSequence assertionValue) throws DecodeException
  {
    final ByteString normAssertionValue = normalizeAssertionValue(assertionValue);
    return getEqualityAssertion(normAssertionValue);
  }
}
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java
@@ -42,7 +42,6 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.SubstringMatchingRule;
@@ -2146,42 +2145,6 @@
    return assertionValue;
  }
  private ByteString getNormalizedAssertionValue() throws DecodeException
  {
    if (normalizedAssertionValue == null)
    {
      return normalizedAssertionValue = normalizeAssertionValue();
    }
    return normalizedAssertionValue;
  }
  private ByteString normalizeAssertionValue() throws DecodeException
  {
    switch (filterType)
    {
    case EQUALITY:
      return attributeType.getEqualityMatchingRule()
          .normalizeAssertionValue(assertionValue);
    case GREATER_OR_EQUAL:
    case LESS_OR_EQUAL:
      return attributeType.getOrderingMatchingRule()
          .normalizeAssertionValue(assertionValue);
    case APPROXIMATE_MATCH:
      return attributeType.getApproximateMatchingRule()
          .normalizeAssertionValue(assertionValue);
    case AND:
    case OR:
    case NOT:
    case PRESENT:
    case SUBSTRING:
    case EXTENSIBLE_MATCH:
    default:
      return null;
    }
  }
  /**
   * Retrieves the subInitial element for this substring filter.
   *
@@ -3610,7 +3573,7 @@
      case APPROXIMATE_MATCH:
        return typeAndOptionsAndAssertionEqual(f);
      case EXTENSIBLE_MATCH:
        return equalsExtensible(f);
        return extensibleEqual(f);
      default:
        return false;
    }
@@ -3641,23 +3604,15 @@
  private boolean typeAndOptionsAndAssertionEqual(SearchFilter f)
  {
    final boolean tmp =
        attributeType.equals(f.attributeType)
            && optionsEqual(attributeOptions, f.attributeOptions);
    try
    {
      return tmp && getNormalizedAssertionValue().equals(f.getNormalizedAssertionValue());
    }
    catch (DecodeException e)
    {
      return tmp && assertionValue.equals(f.assertionValue);
    }
    return attributeType.equals(f.attributeType)
        && optionsEqual(attributeOptions, f.attributeOptions)
        && assertionValue.equals(f.assertionValue);
  }
  private boolean substringEqual(SearchFilter f)
  private boolean substringEqual(SearchFilter other)
  {
    if (! attributeType.equals(f.attributeType))
    if (! attributeType.equals(other.attributeType))
    {
      return false;
    }
@@ -3667,20 +3622,43 @@
    {
      return false;
    }
    try
    {
      Assertion thisAssertion = rule.getSubstringAssertion(subInitialElement, subAnyElements, subFinalElement);
      Assertion thatAssertion = rule.getSubstringAssertion(f.subInitialElement, f.subAnyElements, f.subFinalElement);
      return thisAssertion.equals(thatAssertion);
    }
    catch (DecodeException e)
    if (! optionsEqual(attributeOptions, other.attributeOptions))
    {
      return false;
    }
    boolean initialCheck = subInitialElement == null ?
        other.subInitialElement == null : subInitialElement.equals(other.subInitialElement);
    if (!initialCheck)
    {
      return false;
    }
    boolean finalCheck = subFinalElement == null ?
        other.subFinalElement == null : subFinalElement.equals(other.subFinalElement);
    if (!finalCheck)
    {
      return false;
    }
    boolean anyCheck = subAnyElements == null ?
        other.subAnyElements == null : subAnyElements.size() == other.subAnyElements.size();
    if (!anyCheck)
    {
      return false;
    }
    if (subAnyElements != null)
    {
      for (int i = 0; i < subAnyElements.size(); i++)
      {
        if (! subAnyElements.get(i).equals(other.subAnyElements.get(i)))
        {
          return false;
        }
      }
    }
    return true;
  }
  private boolean equalsExtensible(SearchFilter f)
  private boolean extensibleEqual(SearchFilter f)
  {
    if (attributeType == null)
    {
@@ -3886,41 +3864,28 @@
  private int typeAndAssertionHashCode()
  {
    final int hashCode = attributeType.hashCode();
    try
    {
      return hashCode + getNormalizedAssertionValue().hashCode();
    }
    catch (DecodeException e)
    {
      return hashCode + assertionValue.hashCode();
    }
    return attributeType.hashCode() + assertionValue.hashCode();
  }
  /** Returns hash code to use for substring filter. */
  private int substringHashCode()
  {
    int hashCode = attributeType.hashCode();
    final MatchingRule rule = attributeType.getSubstringMatchingRule();
    if (rule != null)
    if (subInitialElement != null)
    {
      try
      hashCode += subInitialElement.hashCode();
    }
    if (subAnyElements != null)
    {
      for (ByteString e : subAnyElements)
      {
        return hashCode + rule.getSubstringAssertion(subInitialElement, subAnyElements, subFinalElement).hashCode();
      }
      catch (DecodeException e)
      {
        logger.traceException(e);
        hashCode += e.hashCode();
      }
    }
    // Fallback to hash code based on elements
    hashCode += subInitialElement.hashCode();
    for (ByteString e : subAnyElements)
    if (subFinalElement != null)
    {
      hashCode += e.hashCode();
      hashCode += subFinalElement.hashCode();
    }
    hashCode += subFinalElement.hashCode();
    return hashCode;
  }
opendj-sdk/opendj3-server-dev/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
@@ -32,12 +32,10 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.config.ConfigConstants;
import org.opends.server.controls.*;
@@ -1247,17 +1245,7 @@
  private static int extractChangeNumber(SearchFilter sf)
      throws DirectoryException
  {
    try
    {
      MatchingRule rule = sf.getAttributeType().getEqualityMatchingRule();
      ByteString normValue = rule.normalizeAssertionValue(sf.getAssertionValue());
      return Integer.decode(normValue.toString());
    }
    catch (DecodeException e)
    {
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, e
          .getMessageObject(), e);
    }
    return Integer.decode(sf.getAssertionValue().toString());
  }
  private static boolean matches(SearchFilter sf, FilterType filterType,
opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -47,12 +47,6 @@
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.types.*;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.RDN;
import org.opends.server.util.Base64;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -647,15 +641,75 @@
  @Test(dependsOnMethods = "testAdd")
  public void testSearchIndex() throws Exception {
    InternalClientConnection conn =
        InternalClientConnection.getRootConnection();
    InternalClientConnection conn = InternalClientConnection.getRootConnection();
    Set<String> attribs = new LinkedHashSet<String>();
    String debugString;
    int finalStartPos;
    int finalEndPos;
    int finalCount;
    List<SearchResultEntry> result;
    // search 1
    result = doSubtreeSearch(conn, "(&(cn=Aaccf Amar)(cn=Ardyth Bainton))", attribs);
    assertEquals(result.size(), 0);
    // Adding a debug search attribute for next searches
    attribs.add(ATTR_DEBUG_SEARCH_INDEX);
    // search 2
    result = doSubtreeSearch(conn, "(&(cn=Aaccf Amar)(employeeNumber=222))", attribs);
    // Only one index should be used because it is below the FILTER_CANDIDATE
    debugString = getDebugString(result);
    assertTrue(debugString.split("cn").length <= 3);
    assertResultsCountIs(1, debugString);
    // search 3
    result = doSubtreeSearch(conn, "(|(cn=Aaccf Amar)(cn=Ardyth Bainton))", attribs);
    debugString = getDebugString(result);
    assertFalse(debugString.contains("NOT-INDEXED"));
    assertResultsCountIs(2, debugString);
    // search 4
    result = doSubtreeSearch(conn, "(&(employeeNumber=*)(cn=A*)(employeeNumber>=0)(employeeNumber<=z))", attribs);
    debugString = getDebugString(result);
    assertFalse(debugString.contains("NOT-INDEXED"));
    assertResultsCountIs(12, debugString);
    // search 5
    result = doSubtreeSearch(conn,
        "(&(employeeNumber<=z)(cn<=Abbey Abbie)(cn>=0)(|(cn>=Abahri Abazari)(employeeNumber<=9)))", attribs);
    debugString = getDebugString(result);
    assertFalse(debugString.contains("NOT-INDEXED"));
    assertResultsCountIs(11, debugString);
    // search 6
    result = doSubtreeSearch(conn, "(cn~=Aartjan)", attribs);
    debugString = getDebugString(result);
    assertFalse(debugString.contains("NOT-INDEXED"));
    assertResultsCountIs(1, debugString);
  }
  private void assertResultsCountIs(int expectedCount, String debugString)
  {
    int finalStartPos = debugString.indexOf("final=") + 13;
    int finalEndPos = debugString.indexOf("]", finalStartPos);
    int finalCount = Integer.valueOf(debugString.substring(finalStartPos, finalEndPos));
    assertEquals(finalCount, expectedCount);
  }
  /** Returns the debug string from a search result. */
  private String getDebugString(List<SearchResultEntry> result)
  {
    return result.get(0).getAttribute("debugsearchindex").get(0).toString();
  }
  /** Returns the results of subtree search on provided connection with provided filter. */
  private List<SearchResultEntry> doSubtreeSearch(InternalClientConnection conn, String filter,
      Set<String> attribs) throws Exception
  {
    InternalSearchOperation search =
        conn.processSearch(DN.valueOf("dc=test,dc=com"),
            SearchScope.WHOLE_SUBTREE,
@@ -663,111 +717,9 @@
            0,
            0,
            false,
            LDAPFilter.decode("(&(cn=Aaccf Amar)(cn=Ardyth Bainton))").toSearchFilter(),
            LDAPFilter.decode(filter).toSearchFilter(),
            attribs);
    List<SearchResultEntry> result = search.getSearchEntries();
    assertEquals(result.size(), 0);
    attribs.add(ATTR_DEBUG_SEARCH_INDEX);
    search = conn.processSearch(DN.valueOf("dc=test,dc=com"),
        SearchScope.WHOLE_SUBTREE,
        DereferenceAliasesPolicy.NEVER,
        0,
        0,
        false,
        LDAPFilter.decode("(&(cn=Aaccf Amar)(employeeNumber=222))").toSearchFilter(),
        attribs);
    result = search.getSearchEntries();
    //Only one index should be used because it is below the FILTER_CANDIDATEassertEquals(ec.getDN2URI().)_THRESHOLD.
    debugString =
        result.get(0).getAttribute("debugsearchindex").get(0).toString();
    assertTrue(debugString.split("cn").length <= 3);
    finalStartPos = debugString.indexOf("final=") + 13;
    finalEndPos = debugString.indexOf("]", finalStartPos);
    finalCount = Integer.valueOf(debugString.substring(finalStartPos,
        finalEndPos));
    assertEquals(finalCount, 1);
    search = conn.processSearch(DN.valueOf("dc=test,dc=com"),
        SearchScope.WHOLE_SUBTREE,
        DereferenceAliasesPolicy.NEVER,
        0,
        0,
        false,
        LDAPFilter.decode("(|(cn=Aaccf Amar)(cn=Ardyth Bainton))").toSearchFilter(),
        attribs);
    result = search.getSearchEntries();
    debugString =
        result.get(0).getAttribute("debugsearchindex").get(0).toString();
    assertTrue(!debugString.contains("NOT-INDEXED"));
    finalStartPos = debugString.indexOf("final=") + 13;
    finalEndPos = debugString.indexOf("]", finalStartPos);
    finalCount = Integer.valueOf(debugString.substring(finalStartPos,
        finalEndPos));
    assertEquals(finalCount, 2);
    search = conn.processSearch(DN.valueOf("dc=test,dc=com"),
        SearchScope.WHOLE_SUBTREE,
        DereferenceAliasesPolicy.NEVER,
        0,
        0,
        false,
        LDAPFilter.decode("(&(employeeNumber=*)(cn=A*)(employeeNumber>=0)(employeeNumber<=z))").toSearchFilter(),
        attribs);
    result = search.getSearchEntries();
    debugString =
        result.get(0).getAttribute("debugsearchindex").get(0).toString();
    assertTrue(!debugString.contains("NOT-INDEXED"));
    finalStartPos = debugString.indexOf("final=") + 13;
    finalEndPos = debugString.indexOf("]", finalStartPos);
    finalCount = Integer.valueOf(debugString.substring(finalStartPos,
        finalEndPos));
    assertEquals(finalCount, 12);
    search = conn.processSearch(DN.valueOf("dc=test,dc=com"),
        SearchScope.WHOLE_SUBTREE,
        DereferenceAliasesPolicy.NEVER,
        0,
        0,
        false,
        LDAPFilter.decode("(&(employeeNumber<=z)(cn<=Abbey Abbie)(cn>=0)(|(cn>=Abahri Abazari)(employeeNumber<=9)))").toSearchFilter(),
        attribs);
    result = search.getSearchEntries();
    debugString =
        result.get(0).getAttribute("debugsearchindex").get(0).toString();
    assertTrue(!debugString.contains("NOT-INDEXED"));
    finalStartPos = debugString.indexOf("final=") + 13;
    finalEndPos = debugString.indexOf("]", finalStartPos);
    finalCount = Integer.valueOf(debugString.substring(finalStartPos,
        finalEndPos));
    assertEquals(finalCount, 11);
    search = conn.processSearch(DN.valueOf("dc=test,dc=com"),
        SearchScope.WHOLE_SUBTREE,
        DereferenceAliasesPolicy.NEVER,
        0,
        0,
        false,
        LDAPFilter.decode("(cn~=Aartjan)").toSearchFilter(),
        attribs);
    result = search.getSearchEntries();
    debugString =
        result.get(0).getAttribute("debugsearchindex").get(0).toString();
    assertTrue(!debugString.contains("NOT-INDEXED"));
    finalStartPos = debugString.indexOf("final=") + 13;
    finalEndPos = debugString.indexOf("]", finalStartPos);
    finalCount = Integer.valueOf(debugString.substring(finalStartPos,
        finalEndPos));
    assertEquals(finalCount, 1);
    return search.getSearchEntries();
  }
  @Test(dependsOnMethods = {"testAdd", "testSearchIndex",
opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
@@ -918,9 +918,12 @@
          // These should be case insensitive
          {"(SN=Smith)", "(sn=Smith)", true, true},
          {"(sn=smith)", "(sn=Smith)", true, false},
          {"(SN=S*th)", "(sn=S*th)", true, true},
          // We no longer normalize assertion values,
          // so these filters are not equal
          {"(sn=smith)", "(sn=Smith)", false, false},
          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=Smith)", true, true},
          // This demonstrates bug 704.
@@ -939,8 +942,10 @@
          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
          // This demonstrates bug 705.
          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
          // This was initially to demonstrates bug 705.
          // But we reverted back to old behavior,
          // because we no longer normalize assertion values.
          {"(sn=s*t*h)", "(sn=S*T*H)", false, false},
          // These should be case sensitive
          {"(labeledURI=http://opends.org)", "(labeledURI=http://OpenDS.org)", false, false},