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

Jean-Noel Rouvignac
07.58.2014 3ab9614db1a1a1a30c271424c4189999bf71b87a
OPENDJ-1308 Migrate schema support

Moved JE backend indexer code closer to the SDK's IndexQueryFactory.
Removed a lot of duplicated code by relying more on the ExtensibleIndexer.



ExtensibleIndexer.java:
Removed getKeys(AttributeValue, Map<byte[], Boolean>, Boolean) + Moved its code to JEExtensibleIndexer.
Moved computeModifiedKeys() to JEExtensibleIndexer.


ApproximateIndexer.java, EqualityIndexer.java, OrderingIndexer.java, SubstringIndexer.java:
Now extends ExtensibleIndexer.
Removed all code made redundant by new superclass.
Added getIndexID() (not implemented).
Added getExtensibleIndexID().
Kept getKeys(AttributeValue, Set<byte[]>).

CollationMatchingRuleFactory.java:
Extracted method addLengthAndBytes().
Inlined substringKeys(ByteString, Set<byte[]>) code into CollationSubstringExtensibleIndexer.getKeys().
Removed substringKeys(ByteString, Map<byte[], Boolean>, Boolean) and CollationSubstringExtensibleIndexer.getKeys(AttributeValue, Map<byte[], Boolean>), duplicates of ExtensibleIndexer.getKeys(AttributeValue, Map<byte[], Boolean>, Boolean).

JEExtensibleIndexer.java:
In ctor, accepted MatchingRule instead of ExtensibleMatchingRule.
In indexAttribute(List<Attribute>, Map<byte[], Boolean>, Boolean), called indexAttribute(List<Attribute>, Set<byte[]>) + put code from ExtensibleIndexer.getKeys(AttributeValue, Map<byte[], Boolean>, Boolean) here.
Moved ExtensibleIndexer.computeModifiedKeys() here.


AttributeIndex.java:
Extracted methods buildExtIndex(), openNewIndex(), open(), addEntry(), removeEntry(), modifyEntry(), setTrusted(), setRebuildStatus(), addIfNotNull().
In close(), used Utils.closeSilently().
Extracted method evaluateOrderingFilter() to factorize code for evaluateGreaterOrEqualFilter() and evaluateLessOrEqualFilter().
In applyConfigurationChange(), removed all catch(DatabaseException) because they are duplicating the larger catch(Exception) in this method + made adminActionRequired an AtomicBoolean to use it as in/out parameter.


DatabaseContainer.java:
Now implements Closeable + Made close() public.

NullIndex.java:
Made close() public.
Removed all synchronized keywords, totally unnecessary here.

TestBackendImpl.java:
Consequence of all the changes above.
Used static imports.
11 files modified
2009 ■■■■ changed files
opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java 54 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java 164 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java 847 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java 24 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java 176 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java 45 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java 164 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java 157 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java 184 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java 112 ●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java 82 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java
@@ -27,8 +27,6 @@
package org.opends.server.api;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -86,56 +84,4 @@
    throw new RuntimeException("Not implemented yet");
  }
  /**
   * Generates a map of index keys and a boolean flag indicating
   * whether the corresponding key will be inserted or deleted.
   *
   * @param attrValue
   *          The attribute for which keys are required.
   * @param modifiedKeys
   *          A map containing the keys and a boolean. Keys
   *          corresponding to the boolean value <code>true</code>
   *          should be inserted and <code>false</code> should be
   *          deleted.
   * @param insert
   *          <code>true</code> if generated keys should be inserted
   *          or <code>false</code> otherwise.
   */
  public void getKeys(AttributeValue attrValue, Map<byte[], Boolean> modifiedKeys, Boolean insert)
  {
    final Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(attrValue, keys);
    computeModifiedKeys(modifiedKeys, insert, keys);
  }
  /**
   * Computes the modified keys by an indexer.
   *
   * @param modifiedKeys
   *          A map containing the keys and a boolean. Keys
   *          corresponding to the boolean value <code>true</code>
   *          should be inserted and <code>false</code> should be
   *          deleted.
   * @param insert
   *          <code>true</code> if generated keys should be inserted
   *          or <code>false</code> otherwise.
   * @param keys
   *          the newly generated keys that will be added or removed from the Map
   */
  public static void computeModifiedKeys(Map<byte[], Boolean> modifiedKeys,
      Boolean insert, final Set<byte[]> keys)
  {
    for (byte[] key : keys)
    {
      Boolean cInsert = modifiedKeys.get(key);
      if (cInsert == null)
      {
        modifiedKeys.put(key, insert);
      }
      else if (!cInsert.equals(insert))
      {
        modifiedKeys.remove(key);
      }
    }
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
@@ -26,43 +26,28 @@
 */
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.DecodeException;
import org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.types.*;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
/**
 * An implementation of an Indexer for attribute approximate matching.
 */
public class ApproximateIndexer extends Indexer
public class ApproximateIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
  /**
   * The attribute type approximate matching rule.
   */
  private ApproximateMatchingRule approximateRule;
    /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  /**
   * Create a new attribute approximate indexer for the given index
   * configuration.
   * @param attributeType The attribute type for which an indexer is
@@ -70,121 +55,31 @@
   */
  public ApproximateIndexer(AttributeType attributeType)
  {
    this.attributeType = attributeType;
    this.approximateRule = attributeType.getApproximateMatchingRule();
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".approximate";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return comparator;
    return "approximate";
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param keys The set into which the keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, keys);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Set<byte[]> keys)
  public void getKeys(AttributeValue value, Set<byte[]> keys)
  {
    try
    {
      byte[] keyBytes =
           approximateRule.normalizeAttributeValue(value.getValue()).toByteArray();
      keys.add(keyBytes);
      keys.add(approximateRule.normalizeAttributeValue(value.getValue()).toByteArray());
    }
    catch (DecodeException e)
    {
@@ -192,37 +87,4 @@
    }
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, modifiedKeys, insert);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Map<byte[], Boolean> modifiedKeys,
      Boolean insert)
  {
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -26,7 +26,9 @@
 *      Portions Copyright 2014 Manuel Gaupp
 */
package org.opends.server.backends.jeb;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -35,6 +37,7 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
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.server.LocalDBIndexCfg;
@@ -151,20 +154,8 @@
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.EQUALITY))
    {
      if (attrType.getEqualityMatchingRule() == null)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, "equality"));
      }
      Indexer equalityIndexer = new EqualityIndexer(attrType);
      this.equalityIndex = new Index(name + ".equality",
                                     equalityIndexer,
                                     state,
                                     indexEntryLimit,
                                     cursorEntryLimit,
                                     false,
                                     env,
                                     entryContainer);
      this.equalityIndex = buildExtIndex(
          name, attrType, attrType.getEqualityMatchingRule(), new EqualityIndexer());
    }
    if (indexConfig.getIndexType().contains(
@@ -184,57 +175,21 @@
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.SUBSTRING))
    {
      if (attrType.getSubstringMatchingRule() == null)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, "substring"));
      }
      Indexer substringIndexer = new SubstringIndexer(attrType, config);
      this.substringIndex = new Index(name + ".substring",
                                     substringIndexer,
                                     state,
                                     indexEntryLimit,
                                     cursorEntryLimit,
                                     false,
                                     env,
                                     entryContainer);
      this.substringIndex = buildExtIndex(
          name, attrType, attrType.getSubstringMatchingRule(), new SubstringIndexer(attrType, config));
    }
    if (indexConfig.getIndexType().contains(
            LocalDBIndexCfgDefn.IndexType.ORDERING))
    {
      if (attrType.getOrderingMatchingRule() == null)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, "ordering"));
      }
      Indexer orderingIndexer = new OrderingIndexer(attrType);
      this.orderingIndex = new Index(name + ".ordering",
                                     orderingIndexer,
                                     state,
                                     indexEntryLimit,
                                     cursorEntryLimit,
                                     false,
                                     env,
                                     entryContainer);
      this.orderingIndex = buildExtIndex(
          name, attrType, attrType.getOrderingMatchingRule(), new OrderingIndexer(attrType));
    }
    if (indexConfig.getIndexType().contains(
        LocalDBIndexCfgDefn.IndexType.APPROXIMATE))
    {
      if (attrType.getApproximateMatchingRule() == null)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, "approximate"));
      }
      Indexer approximateIndexer = new ApproximateIndexer(attrType);
      this.approximateIndex = new Index(name + ".approximate",
                                        approximateIndexer,
                                        state,
                                        indexEntryLimit,
                                        cursorEntryLimit,
                                        false,
                                        env,
                                        entryContainer);
      this.approximateIndex = buildExtIndex(
          name, attrType, attrType.getApproximateMatchingRule(), new ApproximateIndexer(attrType));
    }
    if (indexConfig.getIndexType().contains(
        LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
@@ -294,6 +249,26 @@
    }
  }
  private Index buildExtIndex(String name, AttributeType attrType,
      MatchingRule rule, ExtensibleIndexer extIndexer) throws ConfigException
  {
    if (rule == null)
    {
      throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
          attrType, extIndexer.getExtensibleIndexID()));
    }
    Indexer indexer = new JEExtensibleIndexer(attrType, rule, extIndexer);
    return new Index(name + "." + extIndexer.getExtensibleIndexID(),
                                   indexer,
                                   state,
                                   indexConfig.getIndexEntryLimit(),
                                   cursorEntryLimit,
                                   false,
                                   env,
                                   entryContainer);
  }
  /**
   * Open the attribute index.
   *
@@ -302,30 +277,11 @@
   */
  public void open() throws DatabaseException
  {
    if (equalityIndex != null)
    {
      equalityIndex.open();
    }
    if (presenceIndex != null)
    {
      presenceIndex.open();
    }
    if (substringIndex != null)
    {
      substringIndex.open();
    }
    if (orderingIndex != null)
    {
      orderingIndex.open();
    }
    if (approximateIndex != null)
    {
      approximateIndex.open();
    }
    open(equalityIndex);
    open(presenceIndex);
    open(substringIndex);
    open(orderingIndex);
    open(approximateIndex);
    if(extensibleIndexes!=null)
    {
@@ -338,6 +294,14 @@
    indexConfig.addChangeListener(this);
  }
  private void open(Index index)
  {
    if (index != null)
    {
      index.open();
    }
  }
  /**
   * Close the attribute index.
   *
@@ -346,37 +310,12 @@
   */
  public void close() throws DatabaseException
  {
    if (equalityIndex != null)
    {
      equalityIndex.close();
    }
    if (presenceIndex != null)
    {
      presenceIndex.close();
    }
    if (substringIndex != null)
    {
      substringIndex.close();
    }
    if (orderingIndex != null)
    {
      orderingIndex.close();
    }
    if (approximateIndex != null)
    {
      approximateIndex.close();
    }
    Utils.closeSilently(equalityIndex, presenceIndex, substringIndex,
        orderingIndex, approximateIndex);
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.close();
      }
      Utils.closeSilently(extensibleIndexes.getIndexes());
    }
    indexConfig.removeChangeListener(this);
@@ -416,33 +355,13 @@
                          Entry entry)
       throws DatabaseException, DirectoryException
  {
    boolean success = false;
    boolean success = true;
    if (equalityIndex != null
        && !equalityIndex.addEntry(buffer, entryID, entry))
    {
      success = false;
    }
    if (presenceIndex != null
        && !presenceIndex.addEntry(buffer, entryID, entry))
    {
      success = false;
    }
    if (substringIndex != null
        && !substringIndex.addEntry(buffer, entryID, entry))
    {
      success = false;
    }
    if (orderingIndex != null
        && !orderingIndex.addEntry(buffer, entryID, entry))
    {
      success = false;
    }
    if (approximateIndex != null
        && !approximateIndex.addEntry(buffer, entryID, entry))
    {
      success = false;
    }
    success = addEntry(equalityIndex, buffer, entryID, entry, success);
    success = addEntry(presenceIndex, buffer, entryID, entry, success);
    success = addEntry(substringIndex, buffer, entryID, entry, success);
    success = addEntry(orderingIndex, buffer, entryID, entry, success);
    success = addEntry(approximateIndex, buffer, entryID, entry, success);
    if(extensibleIndexes!=null)
    {
@@ -458,6 +377,15 @@
    return success;
  }
  private boolean addEntry(Index index, IndexBuffer buffer, EntryID entryID,
      Entry entry, boolean success) throws DirectoryException, DatabaseException
  {
    if (index != null && !index.addEntry(buffer, entryID, entry))
    {
      return false;
    }
    return success;
  }
  /**
   * Update the attribute index for a new entry.
@@ -475,27 +403,11 @@
  {
    boolean success = true;
    if (equalityIndex != null && !equalityIndex.addEntry(txn, entryID, entry))
    {
      success = false;
    }
    if (presenceIndex != null && !presenceIndex.addEntry(txn, entryID, entry))
    {
      success = false;
    }
    if (substringIndex != null && !substringIndex.addEntry(txn, entryID, entry))
    {
      success = false;
    }
    if (orderingIndex != null && !orderingIndex.addEntry(txn, entryID, entry))
    {
      success = false;
    }
    if (approximateIndex != null
        && !approximateIndex.addEntry(txn, entryID, entry))
    {
      success = false;
    }
    success = addEntry(equalityIndex, txn, entryID, entry, success);
    success = addEntry(presenceIndex, txn, entryID, entry, success);
    success = addEntry(substringIndex, txn, entryID, entry, success);
    success = addEntry(orderingIndex, txn, entryID, entry, success);
    success = addEntry(approximateIndex, txn, entryID, entry, success);
    if(extensibleIndexes!=null)
    {
@@ -511,6 +423,17 @@
    return success;
  }
  private boolean addEntry(Index index, Transaction txn, EntryID entryID,
      Entry entry, boolean success) throws DirectoryException, DatabaseException
  {
    if (index != null && !index.addEntry(txn, entryID, entry))
    {
      return false;
    }
    return success;
  }
  /**
   * Update the attribute index for a deleted entry.
   *
@@ -524,30 +447,11 @@
                          Entry entry)
       throws DatabaseException, DirectoryException
  {
    if (equalityIndex != null)
    {
      equalityIndex.removeEntry(buffer, entryID, entry);
    }
    if (presenceIndex != null)
    {
      presenceIndex.removeEntry(buffer, entryID, entry);
    }
    if (substringIndex != null)
    {
      substringIndex.removeEntry(buffer, entryID, entry);
    }
    if (orderingIndex != null)
    {
      orderingIndex.removeEntry(buffer, entryID, entry);
    }
    if(approximateIndex != null)
    {
      approximateIndex.removeEntry(buffer, entryID, entry);
    }
    removeEntry(equalityIndex, buffer, entryID, entry);
    removeEntry(presenceIndex, buffer, entryID, entry);
    removeEntry(substringIndex, buffer, entryID, entry);
    removeEntry(orderingIndex, buffer, entryID, entry);
    removeEntry(approximateIndex, buffer, entryID, entry);
    if(extensibleIndexes!=null)
    {
@@ -558,6 +462,15 @@
    }
  }
  private void removeEntry(Index index, IndexBuffer buffer, EntryID entryID,
      Entry entry) throws DirectoryException, DatabaseException
  {
    if (index != null)
    {
      index.removeEntry(buffer, entryID, entry);
    }
  }
  /**
   * Update the attribute index for a deleted entry.
   *
@@ -570,30 +483,11 @@
  public void removeEntry(Transaction txn, EntryID entryID, Entry entry)
       throws DatabaseException, DirectoryException
  {
    if (equalityIndex != null)
    {
      equalityIndex.removeEntry(txn, entryID, entry);
    }
    if (presenceIndex != null)
    {
      presenceIndex.removeEntry(txn, entryID, entry);
    }
    if (substringIndex != null)
    {
      substringIndex.removeEntry(txn, entryID, entry);
    }
    if (orderingIndex != null)
    {
      orderingIndex.removeEntry(txn, entryID, entry);
    }
    if(approximateIndex != null)
    {
      approximateIndex.removeEntry(txn, entryID, entry);
    }
    removeEntry(equalityIndex, txn, entryID, entry);
    removeEntry(presenceIndex, txn, entryID, entry);
    removeEntry(substringIndex, txn, entryID, entry);
    removeEntry(orderingIndex, txn, entryID, entry);
    removeEntry(approximateIndex, txn, entryID, entry);
    if(extensibleIndexes!=null)
    {
@@ -604,6 +498,15 @@
    }
  }
  private void removeEntry(Index index, Transaction txn, EntryID entryID,
      Entry entry) throws DirectoryException, DatabaseException
  {
    if (index != null)
    {
      index.removeEntry(txn, entryID, entry);
    }
  }
  /**
   * Update the index to reflect a sequence of modifications in a Modify
   * operation.
@@ -623,30 +526,11 @@
                          List<Modification> mods)
       throws DatabaseException
  {
    if (equalityIndex != null)
    {
      equalityIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    if (presenceIndex != null)
    {
      presenceIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    if (substringIndex != null)
    {
      substringIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    if (orderingIndex != null)
    {
      orderingIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    if (approximateIndex != null)
    {
      approximateIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    modifyEntry(equalityIndex, txn, entryID, oldEntry, newEntry, mods);
    modifyEntry(presenceIndex, txn, entryID, oldEntry, newEntry, mods);
    modifyEntry(substringIndex, txn, entryID, oldEntry, newEntry, mods);
    modifyEntry(orderingIndex, txn, entryID, oldEntry, newEntry, mods);
    modifyEntry(approximateIndex, txn, entryID, oldEntry, newEntry, mods);
    if(extensibleIndexes!=null)
    {
@@ -657,6 +541,16 @@
    }
  }
  private void modifyEntry(Index index, Transaction txn, EntryID entryID,
      Entry oldEntry, Entry newEntry, List<Modification> mods)
      throws DatabaseException
  {
    if (index != null)
    {
      index.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
  }
  /**
   * Update the index to reflect a sequence of modifications in a Modify
   * operation.
@@ -676,30 +570,11 @@
                          List<Modification> mods)
       throws DatabaseException
  {
    if (equalityIndex != null)
    {
      equalityIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    if (presenceIndex != null)
    {
      presenceIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    if (substringIndex != null)
    {
      substringIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    if (orderingIndex != null)
    {
      orderingIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    if (approximateIndex != null)
    {
      approximateIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    modifyEntry(equalityIndex, buffer, entryID, oldEntry, newEntry, mods);
    modifyEntry(presenceIndex, buffer, entryID, oldEntry, newEntry, mods);
    modifyEntry(substringIndex, buffer, entryID, oldEntry, newEntry, mods);
    modifyEntry(orderingIndex, buffer, entryID, oldEntry, newEntry, mods);
    modifyEntry(approximateIndex, buffer, entryID, oldEntry, newEntry, mods);
    if(extensibleIndexes!=null)
    {
@@ -710,6 +585,16 @@
    }
  }
  private void modifyEntry(Index index, IndexBuffer buffer, EntryID entryID,
      Entry oldEntry, Entry newEntry, List<Modification> mods)
      throws DatabaseException
  {
    if (index != null)
    {
      index.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
  }
  /**
   * Makes a byte array representing a substring index key for
   * one substring of a value.
@@ -735,8 +620,7 @@
   * @return A set of index keys.
   */
  Set<ByteString> substringKeys(byte[] value)
  {
    // FIXME replace this code with SDK's
  { // FIXME replace this code with SDK's
    // AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys()
    // Eliminate duplicates by putting the keys into a set.
@@ -769,8 +653,7 @@
   * @return The candidate entry IDs.
   */
  private EntryIDSet matchSubstring(byte[] bytes)
  {
    // FIXME replace this code with SDK's
  { // FIXME replace this code with SDK's
    // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.createIndexQuery()
    int substrLength = indexConfig.getSubstringLength();
@@ -855,8 +738,7 @@
   * @return The candidate entry IDs.
   */
  private EntryIDSet matchInitialSubstring(byte[] bytes)
  {
    // FIXME replace this code with SDK's
  { // FIXME replace this code with SDK's
    // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.createIndexQuery()
    // Iterate through all the keys that have this value as the prefix.
@@ -1051,73 +933,9 @@
      SearchFilter filter, 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();
    return evaluateOrderingFilter(filter, true, debugBuffer, monitor);
    }
    try
    {
      // Set the lower bound for a range search.
      // Use the ordering matching rule to normalize the value.
      OrderingMatchingRule orderingRule =
           filter.getAttributeType().getOrderingMatchingRule();
      byte[] lower = orderingRule.normalizeAttributeValue(
           filter.getAssertionValue().getValue()).toByteArray();
      // Set the upper bound to 0 to search all keys greater then the lower
      // bound.
      byte[] upper = new byte[0];
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        debugBuffer.append(indexConfig.getAttribute().getNameOrOID());
        debugBuffer.append(".");
        debugBuffer.append("ordering]");
      }
      // Read the range: lower <= keys < upper.
      EntryIDSet idSet = orderingIndex.readRange(lower, upper, true, false);
      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;
    }
    catch (DecodeException e)
    {
      logger.traceException(e);
      return new EntryIDSet();
    }
  }
  /**
   * Retrieve the entry IDs that might match a less-or-equal filter.
@@ -1135,6 +953,14 @@
                                              StringBuilder debugBuffer,
                                             DatabaseEnvironmentMonitor monitor)
  {
    return evaluateOrderingFilter(filter, false, debugBuffer, monitor);
  }
  private EntryIDSet evaluateOrderingFilter(SearchFilter filter,
      boolean greater, StringBuilder debugBuffer,
      DatabaseEnvironmentMonitor monitor)
  {
    if (orderingIndex == null)
    {
      if(monitor.isFilterUseEnabled())
@@ -1148,17 +974,29 @@
    try
    {
      // Set the lower bound to 0 to start the range search from the smallest
      // key.
      byte[] lower = new byte[0];
      // Set the upper bound for a range search.
      // Use the ordering matching rule to normalize the value.
      OrderingMatchingRule orderingRule =
           filter.getAttributeType().getOrderingMatchingRule();
      byte[] upper = orderingRule.normalizeAttributeValue(
      // FIXME JNR this looks wrong, it should use normalizeAssertionValue()
      byte[] normalizedValue = orderingRule.normalizeAttributeValue(
           filter.getAssertionValue().getValue()).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:");
@@ -1167,8 +1005,8 @@
        debugBuffer.append("ordering]");
      }
      // Read the range: lower < keys <= upper.
      EntryIDSet idSet = orderingIndex.readRange(lower, upper, false, true);
      // Read the range: lower <= keys < upper OR lower < keys <= upper
      EntryIDSet idSet = orderingIndex.readRange(lower, upper, greater, !greater);
      if(monitor.isFilterUseEnabled())
      {
        if(idSet.isDefined())
@@ -1506,6 +1344,7 @@
      ApproximateMatchingRule approximateMatchingRule =
          approximateFilter.getAttributeType().getApproximateMatchingRule();
      // Make a key from the normalized assertion value.
      // FIXME JNR this looks wrong, it should use normalizeAssertionValue()
      byte[] keyBytes =
           approximateMatchingRule.normalizeAttributeValue(
               approximateFilter.getAssertionValue().getValue()).toByteArray();
@@ -1648,30 +1487,11 @@
   */
  public void listDatabases(List<DatabaseContainer> dbList)
  {
    if (equalityIndex != null)
    {
      dbList.add(equalityIndex);
    }
    if (presenceIndex != null)
    {
      dbList.add(presenceIndex);
    }
    if (substringIndex != null)
    {
      dbList.add(substringIndex);
    }
    if (orderingIndex != null)
    {
      dbList.add(orderingIndex);
    }
    if (approximateIndex != null)
    {
      dbList.add(approximateIndex);
    }
    addIfNotNull(dbList, equalityIndex);
    addIfNotNull(dbList, presenceIndex);
    addIfNotNull(dbList, substringIndex);
    addIfNotNull(dbList, orderingIndex);
    addIfNotNull(dbList, approximateIndex);
    if(extensibleIndexes!=null)
    {
@@ -1682,6 +1502,14 @@
    }
  }
  private void addIfNotNull(Collection<? super Index> dbList, Index index)
  {
    if (index != null)
    {
      dbList.add(index);
    }
  }
  /**
   * Get a string representation of this object.
   * @return return A string representation of this object.
@@ -1748,7 +1576,8 @@
  public synchronized ConfigChangeResult applyConfigurationChange(
      LocalDBIndexCfg cfg)
  {
    boolean adminActionRequired = false;
    // this method is not perf sensitive, using an AtomicBoolean will not hurt
    AtomicBoolean adminActionRequired = new AtomicBoolean(false);
    ArrayList<LocalizableMessage> messages = new ArrayList<LocalizableMessage>();
    try
    {
@@ -1762,32 +1591,16 @@
      {
        if (equalityIndex == null)
        {
          // Adding equality index
          Indexer equalityIndexer = new EqualityIndexer(attrType);
          equalityIndex = new Index(name + ".equality",
                                    equalityIndexer,
                                    state,
                                    indexEntryLimit,
                                    cursorEntryLimit,
                                    false,
                                    env,
                                    entryContainer);
          equalityIndex.open();
          if(!equalityIndex.isTrusted())
          {
            adminActionRequired = true;
            messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
                equalityIndex.getName()));
          }
          EqualityIndexer indexer = new EqualityIndexer();
          Indexer extIndexer = new JEExtensibleIndexer(attrType, attrType.getEqualityMatchingRule(), indexer);
          equalityIndex = openNewIndex(name, extIndexer, indexer, adminActionRequired, messages);
        }
        else
        {
          // already exists. Just update index entry limit.
          if(this.equalityIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired = true;
            adminActionRequired.set(true);
            LocalizableMessage message =
                    NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                            equalityIndex.getName());
@@ -1806,13 +1619,6 @@
            entryContainer.deleteDatabase(equalityIndex);
            equalityIndex = null;
          }
          catch(DatabaseException de)
          {
            messages.add(LocalizableMessage.raw(
                    StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -1837,7 +1643,7 @@
          if(!presenceIndex.isTrusted())
          {
            adminActionRequired = true;
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
                presenceIndex.getName()));
          }
@@ -1847,8 +1653,7 @@
          // already exists. Just update index entry limit.
          if(this.presenceIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired = true;
            adminActionRequired.set(true);
            LocalizableMessage message =
                    NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                            presenceIndex.getName());
@@ -1866,13 +1671,6 @@
            entryContainer.deleteDatabase(presenceIndex);
            presenceIndex = null;
          }
          catch(DatabaseException de)
          {
            messages.add(LocalizableMessage.raw(
                    StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -1882,43 +1680,27 @@
      if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.SUBSTRING))
      {
        SubstringIndexer indexer = new SubstringIndexer(attrType, config);
        Indexer extIndexer = new JEExtensibleIndexer(attrType, attrType.getSubstringMatchingRule(), indexer);
        if(substringIndex == null)
        {
          Indexer substringIndexer = new SubstringIndexer(attrType, config);
          substringIndex = new Index(name + ".substring",
                                     substringIndexer,
                                     state,
                                     indexEntryLimit,
                                     cursorEntryLimit,
                                     false,
                                     env,
                                     entryContainer);
          substringIndex.open();
          if(!substringIndex.isTrusted())
          {
            adminActionRequired = true;
            messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
                substringIndex.getName()));
          }
          substringIndex = openNewIndex(name, extIndexer, indexer, adminActionRequired, messages);
        }
        else
        {
          // already exists. Just update index entry limit.
          if(this.substringIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired = true;
            adminActionRequired.set(true);
            LocalizableMessage message =
                    NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                            substringIndex.getName());
            messages.add(message);
          }
          if(indexConfig.getSubstringLength() !=
              cfg.getSubstringLength())
          if (indexConfig.getSubstringLength() != cfg.getSubstringLength())
          {
            Indexer substringIndexer = new SubstringIndexer(attrType, config);
            this.substringIndex.setIndexer(substringIndexer);
            this.substringIndex.setIndexer(extIndexer);
          }
        }
      }
@@ -1932,13 +1714,6 @@
            entryContainer.deleteDatabase(substringIndex);
            substringIndex = null;
          }
          catch(DatabaseException de)
          {
            messages.add(LocalizableMessage.raw(
                    StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -1950,31 +1725,16 @@
      {
        if(orderingIndex == null)
        {
          Indexer orderingIndexer = new OrderingIndexer(attrType);
          orderingIndex = new Index(name + ".ordering",
                                    orderingIndexer,
                                    state,
                                    indexEntryLimit,
                                    cursorEntryLimit,
                                    false,
                                    env,
                                    entryContainer);
          orderingIndex.open();
          if(!orderingIndex.isTrusted())
          {
            adminActionRequired = true;
            messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
                orderingIndex.getName()));
          }
          OrderingIndexer indexer = new OrderingIndexer(attrType);
          Indexer extIndexer = new JEExtensibleIndexer(attrType, attrType.getOrderingMatchingRule(), indexer);
          orderingIndex = openNewIndex(name, extIndexer, indexer, adminActionRequired, messages);
        }
        else
        {
          // already exists. Just update index entry limit.
          if(this.orderingIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired = true;
            adminActionRequired.set(true);
            LocalizableMessage message =
                    NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                            orderingIndex.getName());
@@ -1992,13 +1752,6 @@
            entryContainer.deleteDatabase(orderingIndex);
            orderingIndex = null;
          }
          catch(DatabaseException de)
          {
            messages.add(LocalizableMessage.raw(
                    StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -2011,31 +1764,16 @@
      {
        if(approximateIndex == null)
        {
          Indexer approximateIndexer = new ApproximateIndexer(attrType);
          approximateIndex = new Index(name + ".approximate",
                                       approximateIndexer,
                                       state,
                                       indexEntryLimit,
                                       cursorEntryLimit,
                                       false,
                                       env,
                                       entryContainer);
          approximateIndex.open();
          if(!approximateIndex.isTrusted())
          {
            adminActionRequired = true;
            messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
                approximateIndex.getName()));
          }
          ApproximateIndexer indexer = new ApproximateIndexer(attrType);
          Indexer extIndexer = new JEExtensibleIndexer(attrType, attrType.getApproximateMatchingRule(), indexer);
          approximateIndex = openNewIndex(name, extIndexer, indexer, adminActionRequired, messages);
        }
        else
        {
          // already exists. Just update index entry limit.
          if(this.approximateIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired = true;
            adminActionRequired.set(true);
            LocalizableMessage message =
                    NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                            approximateIndex.getName());
@@ -2053,13 +1791,6 @@
            entryContainer.deleteDatabase(approximateIndex);
            approximateIndex = null;
          }
          catch(DatabaseException de)
          {
            messages.add(
                    LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -2114,7 +1845,7 @@
              if(!extensibleIndex.isTrusted())
              {
                adminActionRequired = true;
                adminActionRequired.set(true);
                messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(
                    extensibleIndex.getName()));
              }
@@ -2124,7 +1855,7 @@
              Index extensibleIndex = extensibleIndexes.getIndex(indexID);
              if(extensibleIndex.setIndexEntryLimit(indexEntryLimit))
              {
                adminActionRequired = true;
                adminActionRequired.set(true);
                LocalizableMessage message =
                      NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                              extensibleIndex.getName());
@@ -2201,13 +1932,6 @@
              }
            }
          }
          catch(DatabaseException de)
          {
            messages.add(
                  LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -2228,13 +1952,6 @@
            }
            extensibleIndexes.deleteAll();
          }
          catch(DatabaseException de)
          {
            messages.add(
                  LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(de)));
            return new ConfigChangeResult(
                DirectoryServer.getServerErrorResultCode(), false, messages);
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
@@ -2244,17 +1961,34 @@
      indexConfig = cfg;
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                    messages);
      return new ConfigChangeResult(
          ResultCode.SUCCESS, adminActionRequired.get(), messages);
    }
    catch(Exception e)
    {
      messages.add(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
      return new ConfigChangeResult(
          DirectoryServer.getServerErrorResultCode(), adminActionRequired, messages);
          DirectoryServer.getServerErrorResultCode(), adminActionRequired.get(), messages);
    }
  }
  private Index openNewIndex(String name, Indexer indexer, ExtensibleIndexer extIndexer,
      AtomicBoolean adminActionRequired, ArrayList<LocalizableMessage> messages)
  {
    Index index = new Index(name + "." + extIndexer.getExtensibleIndexID(), indexer,
        state, indexConfig.getIndexEntryLimit(), cursorEntryLimit, false, env,
        entryContainer);
    index.open();
    if (!index.isTrusted())
    {
      adminActionRequired.set(true);
      messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(index
          .getName()));
    }
    return index;
  }
  /**
   * Set the index truststate.
   * @param txn A database transaction, or null if none is required.
@@ -2265,30 +1999,11 @@
  public synchronized void setTrusted(Transaction txn, boolean trusted)
      throws DatabaseException
  {
    if (equalityIndex != null)
    {
      equalityIndex.setTrusted(txn, trusted);
    }
    if (presenceIndex != null)
    {
      presenceIndex.setTrusted(txn, trusted);
    }
    if (substringIndex != null)
    {
      substringIndex.setTrusted(txn, trusted);
    }
    if (orderingIndex != null)
    {
      orderingIndex.setTrusted(txn, trusted);
    }
    if (approximateIndex != null)
    {
      approximateIndex.setTrusted(txn, trusted);
    }
    setTrusted(equalityIndex, txn, trusted);
    setTrusted(presenceIndex, txn, trusted);
    setTrusted(substringIndex, txn, trusted);
    setTrusted(orderingIndex, txn, trusted);
    setTrusted(approximateIndex, txn, trusted);
    if(extensibleIndexes!=null)
    {
@@ -2299,33 +2014,25 @@
    }
  }
  private void setTrusted(Index index, Transaction txn, boolean trusted)
  {
    if (index != null)
    {
      index.setTrusted(txn, trusted);
    }
  }
  /**
   * Return true iff this index is trusted.
   * @return the trusted state of this index
   */
  public boolean isTrusted()
  {
    if (equalityIndex != null && !equalityIndex.isTrusted())
    {
      return false;
    }
    if (presenceIndex != null && !presenceIndex.isTrusted())
    {
      return false;
    }
    if (substringIndex != null && !substringIndex.isTrusted())
    {
      return false;
    }
    if (orderingIndex != null && !orderingIndex.isTrusted())
    {
      return false;
    }
    if (approximateIndex != null && !approximateIndex.isTrusted())
    if ((equalityIndex != null && !equalityIndex.isTrusted())
        || (presenceIndex != null && !presenceIndex.isTrusted())
        || (substringIndex != null && !substringIndex.isTrusted())
        || (orderingIndex != null && !orderingIndex.isTrusted())
        || (approximateIndex != null && !approximateIndex.isTrusted()))
    {
      return false;
    }
@@ -2351,30 +2058,11 @@
   */
  public synchronized void setRebuildStatus(boolean rebuildRunning)
  {
    if (equalityIndex != null)
    {
      equalityIndex.setRebuildStatus(rebuildRunning);
    }
    if (presenceIndex != null)
    {
      presenceIndex.setRebuildStatus(rebuildRunning);
    }
    if (substringIndex != null)
    {
      substringIndex.setRebuildStatus(rebuildRunning);
    }
    if (orderingIndex != null)
    {
      orderingIndex.setRebuildStatus(rebuildRunning);
    }
    if (approximateIndex != null)
    {
      approximateIndex.setRebuildStatus(rebuildRunning);
    }
    setRebuildStatus(rebuildRunning, equalityIndex);
    setRebuildStatus(rebuildRunning, presenceIndex);
    setRebuildStatus(rebuildRunning, substringIndex);
    setRebuildStatus(rebuildRunning, orderingIndex);
    setRebuildStatus(rebuildRunning, approximateIndex);
    if(extensibleIndexes!=null)
    {
@@ -2385,6 +2073,14 @@
    }
  }
  private void setRebuildStatus(boolean rebuildRunning, Index index)
  {
    if (index != null)
    {
      index.setRebuildStatus(rebuildRunning);
    }
  }
  /**
   * Get the JE database name prefix for indexes in this attribute index.
   *
@@ -2466,30 +2162,11 @@
  public Collection<Index> getAllIndexes() {
    LinkedHashSet<Index> indexes = new LinkedHashSet<Index>();
    if (equalityIndex != null)
    {
      indexes.add(equalityIndex);
    }
    if (presenceIndex != null)
    {
      indexes.add(presenceIndex);
    }
    if (substringIndex != null)
    {
      indexes.add(substringIndex);
    }
    if (orderingIndex != null)
    {
      indexes.add(orderingIndex);
    }
    if (approximateIndex != null)
    {
      indexes.add(approximateIndex);
    }
    addIfNotNull(indexes, equalityIndex);
    addIfNotNull(indexes, presenceIndex);
    addIfNotNull(indexes, substringIndex);
    addIfNotNull(indexes, orderingIndex);
    addIfNotNull(indexes, approximateIndex);
    if(extensibleIndexes!=null)
    {
opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java
@@ -26,17 +26,19 @@
 */
package org.opends.server.backends.jeb;
import com.sleepycat.je.*;
import java.io.Closeable;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.StaticUtils;
import com.sleepycat.je.*;
/**
 * This class is a wrapper around the JE database object and provides basic
 * read and write methods for entries.
 */
public abstract class DatabaseContainer
public abstract class DatabaseContainer implements Closeable
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
@@ -89,7 +91,7 @@
   * created and used to perform the open.
   *
   * @throws DatabaseException if a JE database error occurs while
   * openning the index.
   * opening the index.
   */
  public void open() throws DatabaseException
  {
@@ -131,17 +133,18 @@
   * database container.
   *
   * The database container should not be closed while other processes
   * aquired the container. The container should not be closed
   * acquired the container. The container should not be closed
   * while cursors handles into the database remain open, or
   * transactions that include operations on the database have not yet
   * been commited or aborted.
   * been committed or aborted.
   *
   * The container may not be accessed again after this method is
   * called, regardless of the method's success or failure.
   *
   * @throws DatabaseException if an error occurs.
   */
  synchronized void close() throws DatabaseException
  @Override
  synchronized public void close() throws DatabaseException
  {
    if(dbConfig.getDeferredWrite())
    {
@@ -270,7 +273,6 @@
      throws DatabaseException
  {
    return database.openCursor(cursorConfig);
  }
  /**
@@ -293,6 +295,7 @@
   * Get a string representation of this object.
   * @return return A string representation of this object.
   */
  @Override
  public String toString()
  {
    return name;
@@ -338,7 +341,7 @@
  {
    StringBuilder builder = new StringBuilder();
    builder.append(" (");
    builder.append(status.toString());
    builder.append(status);
    builder.append(")");
    builder.append(" db=");
    try
@@ -347,7 +350,7 @@
    }
    catch (DatabaseException de)
    {
      builder.append(de.toString());
      builder.append(de);
    }
    if (txn != null)
    {
@@ -358,7 +361,7 @@
      }
      catch (DatabaseException de)
      {
        builder.append(de.toString());
        builder.append(de);
      }
    }
    else
@@ -387,5 +390,4 @@
    return builder.toString();
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
@@ -26,159 +26,38 @@
 */
package org.opends.server.backends.jeb;
import java.util.*;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
/**
 * An implementation of an Indexer for attribute equality.
 */
public class EqualityIndexer extends Indexer
public class EqualityIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  /**
   * Create a new attribute equality indexer for the given attribute type.
   * @param attributeType The attribute type for which an indexer is
   * required.
   */
  public EqualityIndexer(AttributeType attributeType)
  {
    this.attributeType = attributeType;
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".equality";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return comparator;
    return "equality";
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param keys The set into which the keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, keys);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Set<byte[]> keys)
  public void getKeys(AttributeValue value, Set<byte[]> keys)
  {
    try
    {
@@ -190,37 +69,4 @@
    }
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, modifiedKeys, insert);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Map<byte[], Boolean> modifiedKeys,
      Boolean insert)
  {
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
@@ -27,12 +27,13 @@
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.MatchingRule;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.Entry;
@@ -77,15 +78,13 @@
   * @param extensibleIndexer The extensible indexer to be used.
   */
  public JEExtensibleIndexer(AttributeType attributeType,
          ExtensibleMatchingRule matchingRule,
          MatchingRule matchingRule,
          ExtensibleIndexer extensibleIndexer)
  {
    this.attributeType = attributeType;
    this.extensibleIndexer = extensibleIndexer;
  }
   /**
   * Gets a string representation of this object.  The returned value is
   * used to name an index created using this object.
@@ -202,14 +201,38 @@
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          extensibleIndexer.getKeys(value, modifiedKeys, insert);
    final Set<byte[]> keys = new HashSet<byte[]>();
    indexAttribute(attrList, keys);
    computeModifiedKeys(modifiedKeys, insert, keys);
        }
  /**
   * Computes a map of index keys and a boolean flag indicating whether the
   * corresponding key will be inserted or deleted.
   *
   * @param modifiedKeys
   *          A map containing the keys and a boolean. Keys corresponding to the
   *          boolean value <code>true</code> should be inserted and
   *          <code>false</code> should be deleted.
   * @param insert
   *          <code>true</code> if generated keys should be inserted or
   *          <code>false</code> otherwise.
   * @param keys
   *          The index keys to map.
   */
  private static void computeModifiedKeys(Map<byte[], Boolean> modifiedKeys,
      Boolean insert, final Set<byte[]> keys)
  {
    for (byte[] key : keys)
    {
      Boolean cInsert = modifiedKeys.get(key);
      if (cInsert == null)
      {
        modifiedKeys.put(key, insert);
      }
      else if (!cInsert.equals(insert))
      {
        modifiedKeys.remove(key);
      }
    }
  }
opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java
@@ -67,9 +67,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean insertID(IndexBuffer buffer, byte[] keyBytes, EntryID entryID)
  {
@@ -78,9 +76,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean insertID(Transaction txn, DatabaseEntry key, EntryID entryID)
      throws DatabaseException
@@ -90,9 +86,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void insert(DatabaseEntry key, ImportIDSet importIdSet,
      DatabaseEntry data) throws DatabaseException
@@ -102,9 +96,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void delete(DatabaseEntry key, ImportIDSet importIdSet,
      DatabaseEntry data) throws DatabaseException
@@ -114,11 +106,9 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized boolean insert(ImportIDSet importIDSet,
  public boolean insert(ImportIDSet importIDSet,
      Set<byte[]> keySet, DatabaseEntry keyData, DatabaseEntry data)
      throws DatabaseException
  {
@@ -127,9 +117,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  void updateKey(Transaction txn, DatabaseEntry key, EntryIDSet deletedIDs,
      EntryIDSet addedIDs) throws DatabaseException
@@ -139,9 +127,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean removeID(IndexBuffer buffer, byte[] keyBytes, EntryID entryID)
  {
@@ -150,9 +136,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void removeID(Transaction txn, DatabaseEntry key, EntryID entryID)
      throws DatabaseException
@@ -162,9 +146,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void delete(Transaction txn, Set<byte[]> keySet, EntryID entryID)
      throws DatabaseException
@@ -174,9 +156,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void delete(IndexBuffer buffer, byte[] keyBytes)
  {
@@ -185,9 +165,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public ConditionResult containsID(Transaction txn, DatabaseEntry key,
      EntryID entryID) throws DatabaseException
@@ -197,9 +175,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public EntryIDSet readKey(DatabaseEntry key, Transaction txn,
      LockMode lockMode)
@@ -209,9 +185,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void writeKey(Transaction txn, DatabaseEntry key,
      EntryIDSet entryIDList) throws DatabaseException
@@ -221,9 +195,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public EntryIDSet readRange(byte[] lower, byte[] upper,
      boolean lowerIncluded, boolean upperIncluded)
@@ -233,9 +205,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public int getEntryLimitExceededCount()
  {
@@ -244,9 +214,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void closeCursor() throws DatabaseException
  {
@@ -255,9 +223,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean addEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -267,9 +233,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean addEntry(Transaction txn, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -279,9 +243,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -291,9 +253,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void removeEntry(Transaction txn, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -303,9 +263,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void modifyEntry(Transaction txn, EntryID entryID, Entry oldEntry,
      Entry newEntry, List<Modification> mods) throws DatabaseException
@@ -315,9 +273,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void modifyEntry(IndexBuffer buffer, EntryID entryID, Entry oldEntry,
      Entry newEntry, List<Modification> mods) throws DatabaseException
@@ -327,9 +283,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean setIndexEntryLimit(int indexEntryLimit)
  {
@@ -338,9 +292,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public int getIndexEntryLimit()
  {
@@ -349,11 +301,9 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized void setTrusted(Transaction txn, boolean trusted)
  public void setTrusted(Transaction txn, boolean trusted)
      throws DatabaseException
  {
    // Do nothing.
@@ -361,42 +311,34 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized boolean isTrusted()
  public boolean isTrusted()
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized boolean isRebuildRunning()
  public boolean isRebuildRunning()
  {
    return false;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized void setRebuildStatus(boolean rebuildRunning)
  public void setRebuildStatus(boolean rebuildRunning)
  {
    // Do nothing.
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean getMaintainCount()
  {
@@ -405,9 +347,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void open() throws DatabaseException
  {
@@ -416,20 +356,16 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  synchronized void close() throws DatabaseException
  public void close() throws DatabaseException
  {
    // Do nothing.
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus put(Transaction txn, DatabaseEntry key,
      DatabaseEntry data) throws DatabaseException
@@ -439,9 +375,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus read(Transaction txn, DatabaseEntry key,
      DatabaseEntry data, LockMode lockMode) throws DatabaseException
@@ -451,9 +385,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus insert(Transaction txn, DatabaseEntry key,
      DatabaseEntry data) throws DatabaseException
@@ -463,9 +395,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus delete(Transaction txn, DatabaseEntry key)
      throws DatabaseException
@@ -475,9 +405,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public Cursor openCursor(Transaction txn, CursorConfig cursorConfig)
      throws DatabaseException
@@ -487,9 +415,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public long getRecordCount() throws DatabaseException
  {
@@ -498,9 +424,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public PreloadStats preload(PreloadConfig config) throws DatabaseException
  {
opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
@@ -26,37 +26,22 @@
 */
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.DecodeException;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
/**
 * An implementation of an Indexer for attribute ordering.
 */
public class OrderingIndexer extends Indexer
public class OrderingIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  /**
   * The attribute type ordering matching rule which is also the
   * comparator for the index keys generated by this class.
@@ -71,115 +56,27 @@
   */
  public OrderingIndexer(AttributeType attributeType)
  {
    this.attributeType = attributeType;
    this.orderingRule = attributeType.getOrderingMatchingRule();
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".ordering";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return orderingRule;
    return "ordering";
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param keys The set into which the keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, keys);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Set<byte[]> keys)
  public void getKeys(AttributeValue value, Set<byte[]> keys)
  {
    try
    {
@@ -191,38 +88,4 @@
    }
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, modifiedKeys, insert);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Map<byte[], Boolean> modifiedKeys,
      Boolean insert)
  {
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
@@ -26,10 +26,6 @@
 */
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -37,28 +33,17 @@
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.types.*;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
/**
 * An implementation of an Indexer for attribute substrings.
 */
public class SubstringIndexer extends Indexer
public class SubstringIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  private SubstringMatchingRule substringRule;
  private IndexingOptions indexingOptions;
  /**
@@ -72,115 +57,23 @@
  public SubstringIndexer(AttributeType attributeType,
      IndexingOptions indexingOptions)
  {
    this.attributeType = attributeType;
    this.substringRule = attributeType.getSubstringMatchingRule();
    this.indexingOptions = indexingOptions;
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".substring";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return comparator;
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of substring index keys for an attribute.
   * @param attrList The attribute for which substring keys are required.
   * @param keys The set into which the generated keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        SubstringMatchingRule rule =
            attr.getAttributeType().getSubstringMatchingRule();
        for (AttributeValue value : attr)
        {
          getKeys(rule, value, keys);
        }
      }
    }
    return "substring";
  }
  /**
@@ -191,12 +84,12 @@
   * @param attrValue A byte array containing the normalized attribute value
   * @param keys A set into which the keys will be inserted.
   */
  private void getKeys(SubstringMatchingRule rule, AttributeValue attrValue,
      Set<byte[]> keys)
  @Override
  public void getKeys(AttributeValue attrValue, Set<byte[]> keys)
  { // TODO merge with ExtensibleIndexer.getKeys(attrValue, keys);
    try
    {
      byte[] value = rule.normalizeAttributeValue(attrValue.getValue()).toByteArray();
      byte[] value = substringRule.normalizeAttributeValue(attrValue.getValue()).toByteArray();
      // Example: The value is ABCDE and the substring length is 3.
      // We produce the keys ABC BCD CDE DE E
@@ -233,51 +126,4 @@
    return keyBytes;
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        SubstringMatchingRule rule =
            attr.getAttributeType().getSubstringMatchingRule();
        for (AttributeValue value : attr)
        {
          getKeys(rule, value, modifiedKeys, insert);
        }
      }
    }
  }
  /**
   * Decompose an attribute value into a set of substring index keys.
   * The ID of the entry containing this value should be inserted
   * into the list of each of these keys.
   *
   * @param value A byte array containing the normalized attribute value
   * @param modifiedKeys The map into which the modified
   *  keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void getKeys(SubstringMatchingRule rule, AttributeValue value,
      Map<byte[], Boolean> modifiedKeys, Boolean insert)
  { // TODO merge with ExtensibleIndexer.getKeys(attrValue, modifiedKeys, insert);
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(rule, value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -1350,7 +1350,6 @@
      // initialLength, initial, numberofany, anyLength1, any1,
      // anyLength2, any2, ..., anyLengthn, anyn, finalLength,
      // final
      CollationKey key;
      List<Integer> normalizedList = new ArrayList<Integer>();
      if (subInitial == null)
@@ -1359,16 +1358,7 @@
      }
      else
      {
        key = collator.getCollationKey(subInitial);
        byte[] initialBytes = key.toByteArray();
        // Last 4 bytes are 0s with PRIMARY strength.
        int length = initialBytes.length - 4;
        normalizedList.add(length);
        for (int i = 0; i < length; i++)
        {
          normalizedList.add((int) initialBytes[i]);
        }
        addLengthAndBytes(subInitial, normalizedList);
      }
      List<String> subAny = assertion.getAny();
@@ -1381,14 +1371,7 @@
        normalizedList.add(subAny.size());
        for (String any : subAny)
        {
          key = collator.getCollationKey(any);
          byte[] anyBytes = key.toByteArray();
          int length = anyBytes.length - 4;
          normalizedList.add(length);
          for (int i = 0; i < length; i++)
          {
            normalizedList.add((int) anyBytes[i]);
          }
          addLengthAndBytes(any, normalizedList);
        }
      }
@@ -1399,14 +1382,7 @@
      }
      else
      {
        key = collator.getCollationKey(subFinal);
        byte[] subFinalBytes = key.toByteArray();
        int length = subFinalBytes.length - 4;
        normalizedList.add(length);
        for (int i = 0; i < length; i++)
        {
          normalizedList.add((int) subFinalBytes[i]);
        }
        addLengthAndBytes(subFinal, normalizedList);
      }
      byte[] normalizedBytes = new byte[normalizedList.size()];
@@ -1420,6 +1396,23 @@
    private void addLengthAndBytes(String substring,
        List<Integer> normalizedList)
    {
      CollationKey key = collator.getCollationKey(substring);
      byte[] substrBytes = key.toByteArray();
      // Last 4 bytes are 0s with PRIMARY strength.
      int length = substrBytes.length - 4;
      normalizedList.add(length);
      for (int i = 0; i < length; i++)
      {
        normalizedList.add((int) substrBytes[i]);
      }
    }
    /**
     * {@inheritDoc}
     */
@@ -1566,50 +1559,6 @@
    }
    /**
     * Decomposes an attribute value into a set of substring index keys.
     *
     * @param attValue
     *          The normalized attribute value
     * @param set
     *          A set into which the keys will be inserted.
     */
    private void substringKeys(ByteString attValue, Set<byte[]> keys)
    { // TODO merge with ExtensibleIndexer.getKeys(attrValue, keys);
      // TODO and with AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys();
      String value = attValue.toString();
      int keyLength = subIndexer.getSubstringLength();
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
      {
        int len = Math.min(keyLength, remain);
        keys.add(makeSubstringKey(value, i, len));
      }
    }
    /**
     * Decomposes an attribute value into a set of substring index keys.
     *
     * @param value
     *          The normalized attribute value
     * @param modifiedKeys
     *          The map into which the modified keys will be inserted.
     * @param insert
     *          <code>true</code> if generated keys should be inserted
     *          or <code>false</code> otherwise.
     */
    private void substringKeys(ByteString attValue,
        Map<byte[], Boolean> modifiedKeys, Boolean insert)
    { // TODO merge with ExtensibleIndexer.getKeys(attrValue, modifiedKeys, insert);
      Set<byte[]> keys = new TreeSet<byte[]>();
      substringKeys(attValue, keys);
      ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
    }
    /**
     * Makes a byte array representing a substring index key for one
     * substring of a value.
@@ -2199,21 +2148,16 @@
     * {@inheritDoc}
     */
    @Override
    public void getKeys(AttributeValue value, Set<byte[]> keys)
    public void getKeys(AttributeValue attValue, Set<byte[]> keys)
    { // TODO merge with ExtensibleIndexer.getKeys(attrValue, keys);
      // TODO and with AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys();
      String value = attValue.toString();
      int keyLength = substringLen;
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
    {
      matchingRule.substringKeys(value.getValue(), keys);
        int len = Math.min(keyLength, remain);
        keys.add(matchingRule.makeSubstringKey(value, i, len));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void getKeys(AttributeValue attValue,
        Map<byte[], Boolean> modifiedKeys, Boolean insert)
    {
      matchingRule.substringKeys(attValue.getValue(), modifiedKeys, insert);
    }
    /** {@inheritDoc} */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -30,7 +30,6 @@
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
@@ -38,6 +37,7 @@
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.admin.std.meta.LocalDBBackendCfgDefn;
import org.opends.server.admin.std.server.LocalDBBackendCfg;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.controls.SubtreeDeleteControl;
import org.opends.server.core.DeleteOperationBasis;
import org.opends.server.core.DirectoryServer;
@@ -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;
@@ -64,7 +58,9 @@
import static org.assertj.core.api.Assertions.*;
import static org.forgerock.opendj.ldap.ConditionResult.*;
import static org.forgerock.opendj.ldap.ModificationType.*;
import static org.mockito.Mockito.*;
import static org.opends.server.types.Attributes.*;
import static org.testng.Assert.*;
/**
@@ -846,7 +842,7 @@
      assertIndexContainsID(presenceIndexer, entry, index.presenceIndex,
          entryID, FALSE);
      Indexer equalityIndexer = new EqualityIndexer(index.getAttributeType());
      Indexer equalityIndexer = newEqualityIndexer(index);
      assertIndexContainsID(equalityIndexer, entry, index.equalityIndex,
          entryID, FALSE);
@@ -854,7 +850,7 @@
      assertIndexContainsID(substringIndexer, entry, index.substringIndex,
          entryID, FALSE);
      Indexer orderingIndexer = new OrderingIndexer(index.getAttributeType());
      Indexer orderingIndexer = newOrderingIndexer(index);
      assertIndexContainsID(orderingIndexer, entry, index.orderingIndex,
          entryID, FALSE);
    }
@@ -864,12 +860,28 @@
    }
  }
  private SubstringIndexer newSubstringIndexer(AttributeIndex index)
  private Indexer newOrderingIndexer(AttributeIndex index)
  {
    AttributeType attrType = index.getAttributeType();
    ExtensibleIndexer extIndexer = new OrderingIndexer(index.getAttributeType());
    return new JEExtensibleIndexer(attrType, attrType.getSubstringMatchingRule(), extIndexer);
  }
  private Indexer newEqualityIndexer(AttributeIndex index)
  {
    AttributeType attrType = index.getAttributeType();
    ExtensibleIndexer extIndexer = new EqualityIndexer();
    return new JEExtensibleIndexer(attrType, attrType.getSubstringMatchingRule(), extIndexer);
  }
  private Indexer newSubstringIndexer(AttributeIndex index)
  {
    final IndexingOptions options = mock(IndexingOptions.class);
    when(options.substringKeySize()).thenReturn(
        index.getConfiguration().getSubstringLength());
    return new SubstringIndexer(index.getAttributeType(), options);
    AttributeType attrType = index.getAttributeType();
    ExtensibleIndexer extIndexer = new SubstringIndexer(attrType, options);
    return new JEExtensibleIndexer(attrType, attrType.getSubstringMatchingRule(), extIndexer);
  }
  private void assertIndexContainsID(Indexer indexer, Entry entry, Index index,
@@ -940,7 +952,7 @@
          entry.getAttribute("cn").get(0).getAttributeType();
      AttributeIndex index = ec.getAttributeIndex(attribute);
      Indexer orderingIndexer = new OrderingIndexer(index.getAttributeType());
      Indexer orderingIndexer = newOrderingIndexer(index);
      assertIndexContainsID(orderingIndexer, entry, index.orderingIndex,
          entryID, TRUE);
      assertIndexContainsID(orderingIndexer, oldEntry, index.orderingIndex,
@@ -952,7 +964,7 @@
      assertIndexContainsID(substringIndexer, oldEntry, index.substringIndex,
          entryID, FALSE);
      Indexer equalityIndexer = new EqualityIndexer(index.getAttributeType());
      Indexer equalityIndexer = newEqualityIndexer(index);
      assertIndexContainsID(equalityIndexer, entry, index.equalityIndex,
          entryID, TRUE);
      assertIndexContainsID(equalityIndexer, oldEntry, index.equalityIndex,
@@ -976,10 +988,10 @@
    AttributeIndex titleIndex;
    AttributeIndex nameIndex;
    Set<byte[]> addKeys;
    PresenceIndexer presenceIndexer;
    EqualityIndexer equalityIndexer;
    SubstringIndexer substringIndexer;
    OrderingIndexer orderingIndexer;
    Indexer presenceIndexer;
    Indexer equalityIndexer;
    Indexer substringIndexer;
    Indexer orderingIndexer;
    EntryContainer ec = backend.getRootContainer().getEntryContainer(
        DN.valueOf("dc=test,dc=com"));
@@ -987,42 +999,32 @@
    try
    {
      List<Modification> modifications = new ArrayList<Modification>();
      modifications.add(new Modification(ModificationType.ADD, Attributes
          .create("title", "debugger")));
      modifications.add(new Modification(ADD, create("title", "debugger")));
      AttributeBuilder builder = new AttributeBuilder("title");
      builder.setOption("lang-en");
      builder.add("debugger2");
      modifications.add(new Modification(ModificationType.ADD, builder
          .toAttribute()));
      modifications.add(new Modification(ModificationType.DELETE,
          Attributes.create("cn", "Aaren Atp")));
      modifications.add(new Modification(ModificationType.ADD, Attributes
          .create("cn", "Aaren Rigor")));
      modifications.add(new Modification(ModificationType.ADD, Attributes
          .create("cn", "Aarenister Rigor")));
      modifications.add(new Modification(ADD, builder.toAttribute()));
      modifications.add(new Modification(DELETE, create("cn", "Aaren Atp")));
      modifications.add(new Modification(ADD, create("cn", "Aaren Rigor")));
      modifications.add(new Modification(ADD, create("cn", "Aarenister Rigor")));
      builder = new AttributeBuilder("givenname");
      builder.add("test");
      builder.setOption("lang-de");
      modifications.add(new Modification(ModificationType.ADD, builder
          .toAttribute()));
      modifications.add(new Modification(ADD, builder.toAttribute()));
      builder = new AttributeBuilder("givenname");
      builder.add("test2");
      builder.setOption("lang-cn");
      modifications.add(new Modification(ModificationType.DELETE, builder
          .toAttribute()));
      modifications.add(new Modification(DELETE, builder.toAttribute()));
      builder = new AttributeBuilder("givenname");
      builder.add("newtest3");
      builder.setOption("lang-es");
      modifications.add(new Modification(ModificationType.REPLACE, builder
          .toAttribute()));
      modifications.add(new Modification(ModificationType.REPLACE,
          Attributes.create("employeenumber", "222")));
      modifications.add(new Modification(REPLACE, builder.toAttribute()));
      modifications.add(new Modification(REPLACE, create("employeenumber", "222")));
      newEntry = entries.get(1);
      newEntry.applyModifications(modifications);
@@ -1097,16 +1099,16 @@
      presenceIndexer = new PresenceIndexer(nameIndex.getAttributeType());
      assertIndexContainsID(presenceIndexer, entry, nameIndex.presenceIndex, entryID);
      orderingIndexer = new OrderingIndexer(titleIndex.getAttributeType());
      orderingIndexer = newOrderingIndexer(titleIndex);
      assertIndexContainsID(orderingIndexer, entry, titleIndex.orderingIndex, entryID);
      orderingIndexer = new OrderingIndexer(nameIndex.getAttributeType());
      orderingIndexer = newOrderingIndexer(nameIndex);
      assertIndexContainsID(orderingIndexer, entry, nameIndex.orderingIndex, entryID);
      equalityIndexer = new EqualityIndexer(titleIndex.getAttributeType());
      equalityIndexer = newEqualityIndexer(titleIndex);
      assertIndexContainsID(equalityIndexer, entry, titleIndex.equalityIndex, entryID);
      equalityIndexer = new EqualityIndexer(nameIndex.getAttributeType());
      equalityIndexer = newEqualityIndexer(nameIndex);
      assertIndexContainsID(equalityIndexer, entry, nameIndex.equalityIndex, entryID);
      substringIndexer = newSubstringIndexer(titleIndex);