From 3ab9614db1a1a1a30c271424c4189999bf71b87a Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 07 Mar 2014 10:58:03 +0000
Subject: [PATCH] OPENDJ-1308 Migrate schema support

---
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java                          |  851 +++++++-----------------
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java |   82 +-
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java                         |  157 ----
 opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java                                |   54 -
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java                      |  164 ----
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java                       |   24 
 opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java                  |  116 --
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java                               |  164 +---
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java                        |  184 ----
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java                     |   43 
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java                         |  176 ----
 11 files changed, 475 insertions(+), 1,540 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java b/opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java
index 304d7f8..3196adf 100644
--- a/opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java
+++ b/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);
-      }
-    }
-  }
 }
\ No newline at end of file
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
index 49db13d..6d4884d 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
+++ b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
@@ -26,42 +26,27 @@
  */
 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.
@@ -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);
-  }
 }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
index c5449f2..841d35d 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
+++ b/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,74 +933,10 @@
       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();
-    }
-
-    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();
-    }
+    return evaluateOrderingFilter(filter, true, debugBuffer, monitor);
   }
 
+
   /**
    * 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,16 +974,28 @@
 
     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(
-           filter.getAssertionValue().getValue()).toByteArray();
+      // 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)
       {
@@ -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)
     {
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java
index a4f58c4..7e39a52 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java
+++ b/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();
   }
 
-
 }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
index 6ae0286..cc287fd 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
+++ b/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);
-  }
 }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
index a8b8c0e..3fa54eb 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
+++ b/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)
+    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)
     {
-      if (!attr.isVirtual())
+      Boolean cInsert = modifiedKeys.get(key);
+      if (cInsert == null)
       {
-        for (AttributeValue value : attr)
-        {
-          extensibleIndexer.getKeys(value, modifiedKeys, insert);
-        }
+        modifiedKeys.put(key, insert);
+      }
+      else if (!cInsert.equals(insert))
+      {
+        modifiedKeys.remove(key);
       }
     }
   }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java
index c266cc3..e7a42a0 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java
+++ b/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
   {
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
index c7b54c2..a55e82e 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
+++ b/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);
-  }
 }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
index 6547454..247aa80 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
+++ b/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);
-  }
-
 }
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java b/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
index a6a5b15..a778a52 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
+++ b/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)
-    {
-      matchingRule.substringKeys(value.getValue(), keys);
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getKeys(AttributeValue attValue,
-        Map<byte[], Boolean> modifiedKeys, Boolean insert)
-    {
-      matchingRule.substringKeys(attValue.getValue(), modifiedKeys, insert);
+    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--)
+      {
+        int len = Math.min(keyLength, remain);
+        keys.add(matchingRule.makeSubstringKey(value, i, len));
+      }
     }
 
     /** {@inheritDoc} */
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
index d1b9c97..bc22017 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
+++ b/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);

--
Gitblit v1.10.0