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

Jean-Noel Rouvignac
07.58.2014 3ab9614db1a1a1a30c271424c4189999bf71b87a
opendj3-server-dev/src/server/org/opends/server/api/ExtensibleIndexer.java
@@ -27,8 +27,6 @@
package org.opends.server.api;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -86,56 +84,4 @@
    throw new RuntimeException("Not implemented yet");
  }
  /**
   * Generates a map of index keys and a boolean flag indicating
   * whether the corresponding key will be inserted or deleted.
   *
   * @param attrValue
   *          The attribute for which keys are required.
   * @param modifiedKeys
   *          A map containing the keys and a boolean. Keys
   *          corresponding to the boolean value <code>true</code>
   *          should be inserted and <code>false</code> should be
   *          deleted.
   * @param insert
   *          <code>true</code> if generated keys should be inserted
   *          or <code>false</code> otherwise.
   */
  public void getKeys(AttributeValue attrValue, Map<byte[], Boolean> modifiedKeys, Boolean insert)
  {
    final Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(attrValue, keys);
    computeModifiedKeys(modifiedKeys, insert, keys);
  }
  /**
   * Computes the modified keys by an indexer.
   *
   * @param modifiedKeys
   *          A map containing the keys and a boolean. Keys
   *          corresponding to the boolean value <code>true</code>
   *          should be inserted and <code>false</code> should be
   *          deleted.
   * @param insert
   *          <code>true</code> if generated keys should be inserted
   *          or <code>false</code> otherwise.
   * @param keys
   *          the newly generated keys that will be added or removed from the Map
   */
  public static void computeModifiedKeys(Map<byte[], Boolean> modifiedKeys,
      Boolean insert, final Set<byte[]> keys)
  {
    for (byte[] key : keys)
    {
      Boolean cInsert = modifiedKeys.get(key);
      if (cInsert == null)
      {
        modifiedKeys.put(key, insert);
      }
      else if (!cInsert.equals(insert))
      {
        modifiedKeys.remove(key);
      }
    }
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
@@ -26,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);
  }
}
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)
    {
opendj3-server-dev/src/server/org/opends/server/backends/jeb/DatabaseContainer.java
@@ -26,17 +26,19 @@
 */
package org.opends.server.backends.jeb;
import com.sleepycat.je.*;
import java.io.Closeable;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.StaticUtils;
import com.sleepycat.je.*;
/**
 * This class is a wrapper around the JE database object and provides basic
 * read and write methods for entries.
 */
public abstract class DatabaseContainer
public abstract class DatabaseContainer implements Closeable
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
@@ -89,7 +91,7 @@
   * created and used to perform the open.
   *
   * @throws DatabaseException if a JE database error occurs while
   * openning the index.
   * opening the index.
   */
  public void open() throws DatabaseException
  {
@@ -131,17 +133,18 @@
   * database container.
   *
   * The database container should not be closed while other processes
   * aquired the container. The container should not be closed
   * acquired the container. The container should not be closed
   * while cursors handles into the database remain open, or
   * transactions that include operations on the database have not yet
   * been commited or aborted.
   * been committed or aborted.
   *
   * The container may not be accessed again after this method is
   * called, regardless of the method's success or failure.
   *
   * @throws DatabaseException if an error occurs.
   */
  synchronized void close() throws DatabaseException
  @Override
  synchronized public void close() throws DatabaseException
  {
    if(dbConfig.getDeferredWrite())
    {
@@ -270,7 +273,6 @@
      throws DatabaseException
  {
    return database.openCursor(cursorConfig);
  }
  /**
@@ -293,6 +295,7 @@
   * Get a string representation of this object.
   * @return return A string representation of this object.
   */
  @Override
  public String toString()
  {
    return name;
@@ -338,7 +341,7 @@
  {
    StringBuilder builder = new StringBuilder();
    builder.append(" (");
    builder.append(status.toString());
    builder.append(status);
    builder.append(")");
    builder.append(" db=");
    try
@@ -347,7 +350,7 @@
    }
    catch (DatabaseException de)
    {
      builder.append(de.toString());
      builder.append(de);
    }
    if (txn != null)
    {
@@ -358,7 +361,7 @@
      }
      catch (DatabaseException de)
      {
        builder.append(de.toString());
        builder.append(de);
      }
    }
    else
@@ -387,5 +390,4 @@
    return builder.toString();
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
@@ -26,159 +26,38 @@
 */
package org.opends.server.backends.jeb;
import java.util.*;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
/**
 * An implementation of an Indexer for attribute equality.
 */
public class EqualityIndexer extends Indexer
public class EqualityIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  /**
   * Create a new attribute equality indexer for the given attribute type.
   * @param attributeType The attribute type for which an indexer is
   * required.
   */
  public EqualityIndexer(AttributeType attributeType)
  {
    this.attributeType = attributeType;
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".equality";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return comparator;
    return "equality";
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param keys The set into which the keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, keys);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Set<byte[]> keys)
  public void getKeys(AttributeValue value, Set<byte[]> keys)
  {
    try
    {
@@ -190,37 +69,4 @@
    }
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, modifiedKeys, insert);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Map<byte[], Boolean> modifiedKeys,
      Boolean insert)
  {
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
@@ -27,12 +27,13 @@
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.MatchingRule;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.Entry;
@@ -77,15 +78,13 @@
   * @param extensibleIndexer The extensible indexer to be used.
   */
  public JEExtensibleIndexer(AttributeType attributeType,
          ExtensibleMatchingRule matchingRule,
          MatchingRule matchingRule,
          ExtensibleIndexer extensibleIndexer)
  {
    this.attributeType = attributeType;
    this.extensibleIndexer = extensibleIndexer;
  }
   /**
   * Gets a string representation of this object.  The returned value is
   * used to name an index created using this object.
@@ -202,14 +201,38 @@
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    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);
      }
    }
  }
opendj3-server-dev/src/server/org/opends/server/backends/jeb/NullIndex.java
@@ -67,9 +67,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean insertID(IndexBuffer buffer, byte[] keyBytes, EntryID entryID)
  {
@@ -78,9 +76,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean insertID(Transaction txn, DatabaseEntry key, EntryID entryID)
      throws DatabaseException
@@ -90,9 +86,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void insert(DatabaseEntry key, ImportIDSet importIdSet,
      DatabaseEntry data) throws DatabaseException
@@ -102,9 +96,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void delete(DatabaseEntry key, ImportIDSet importIdSet,
      DatabaseEntry data) throws DatabaseException
@@ -114,11 +106,9 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized boolean insert(ImportIDSet importIDSet,
  public boolean insert(ImportIDSet importIDSet,
      Set<byte[]> keySet, DatabaseEntry keyData, DatabaseEntry data)
      throws DatabaseException
  {
@@ -127,9 +117,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  void updateKey(Transaction txn, DatabaseEntry key, EntryIDSet deletedIDs,
      EntryIDSet addedIDs) throws DatabaseException
@@ -139,9 +127,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean removeID(IndexBuffer buffer, byte[] keyBytes, EntryID entryID)
  {
@@ -150,9 +136,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void removeID(Transaction txn, DatabaseEntry key, EntryID entryID)
      throws DatabaseException
@@ -162,9 +146,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void delete(Transaction txn, Set<byte[]> keySet, EntryID entryID)
      throws DatabaseException
@@ -174,9 +156,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void delete(IndexBuffer buffer, byte[] keyBytes)
  {
@@ -185,9 +165,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public ConditionResult containsID(Transaction txn, DatabaseEntry key,
      EntryID entryID) throws DatabaseException
@@ -197,9 +175,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public EntryIDSet readKey(DatabaseEntry key, Transaction txn,
      LockMode lockMode)
@@ -209,9 +185,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void writeKey(Transaction txn, DatabaseEntry key,
      EntryIDSet entryIDList) throws DatabaseException
@@ -221,9 +195,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public EntryIDSet readRange(byte[] lower, byte[] upper,
      boolean lowerIncluded, boolean upperIncluded)
@@ -233,9 +205,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public int getEntryLimitExceededCount()
  {
@@ -244,9 +214,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void closeCursor() throws DatabaseException
  {
@@ -255,9 +223,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean addEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -267,9 +233,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean addEntry(Transaction txn, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -279,9 +243,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -291,9 +253,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void removeEntry(Transaction txn, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
@@ -303,9 +263,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void modifyEntry(Transaction txn, EntryID entryID, Entry oldEntry,
      Entry newEntry, List<Modification> mods) throws DatabaseException
@@ -315,9 +273,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void modifyEntry(IndexBuffer buffer, EntryID entryID, Entry oldEntry,
      Entry newEntry, List<Modification> mods) throws DatabaseException
@@ -327,9 +283,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean setIndexEntryLimit(int indexEntryLimit)
  {
@@ -338,9 +292,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public int getIndexEntryLimit()
  {
@@ -349,11 +301,9 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized void setTrusted(Transaction txn, boolean trusted)
  public void setTrusted(Transaction txn, boolean trusted)
      throws DatabaseException
  {
    // Do nothing.
@@ -361,42 +311,34 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized boolean isTrusted()
  public boolean isTrusted()
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized boolean isRebuildRunning()
  public boolean isRebuildRunning()
  {
    return false;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public synchronized void setRebuildStatus(boolean rebuildRunning)
  public void setRebuildStatus(boolean rebuildRunning)
  {
    // Do nothing.
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean getMaintainCount()
  {
@@ -405,9 +347,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void open() throws DatabaseException
  {
@@ -416,20 +356,16 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  synchronized void close() throws DatabaseException
  public void close() throws DatabaseException
  {
    // Do nothing.
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus put(Transaction txn, DatabaseEntry key,
      DatabaseEntry data) throws DatabaseException
@@ -439,9 +375,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus read(Transaction txn, DatabaseEntry key,
      DatabaseEntry data, LockMode lockMode) throws DatabaseException
@@ -451,9 +385,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus insert(Transaction txn, DatabaseEntry key,
      DatabaseEntry data) throws DatabaseException
@@ -463,9 +395,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  protected OperationStatus delete(Transaction txn, DatabaseEntry key)
      throws DatabaseException
@@ -475,9 +405,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public Cursor openCursor(Transaction txn, CursorConfig cursorConfig)
      throws DatabaseException
@@ -487,9 +415,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public long getRecordCount() throws DatabaseException
  {
@@ -498,9 +424,7 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public PreloadStats preload(PreloadConfig config) throws DatabaseException
  {
opendj3-server-dev/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
@@ -26,37 +26,22 @@
 */
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.DecodeException;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
/**
 * An implementation of an Indexer for attribute ordering.
 */
public class OrderingIndexer extends Indexer
public class OrderingIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  /**
   * The attribute type ordering matching rule which is also the
   * comparator for the index keys generated by this class.
@@ -71,115 +56,27 @@
   */
  public OrderingIndexer(AttributeType attributeType)
  {
    this.attributeType = attributeType;
    this.orderingRule = attributeType.getOrderingMatchingRule();
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".ordering";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return orderingRule;
    return "ordering";
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param keys The set into which the keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, keys);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Set<byte[]> keys)
  public void getKeys(AttributeValue value, Set<byte[]> keys)
  {
    try
    {
@@ -191,38 +88,4 @@
    }
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        for (AttributeValue value : attr)
        {
          getKeys(value, modifiedKeys, insert);
        }
      }
    }
  }
  private void getKeys(AttributeValue value, Map<byte[], Boolean> modifiedKeys,
      Boolean insert)
  {
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
@@ -26,10 +26,6 @@
 */
package org.opends.server.backends.jeb;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -37,28 +33,17 @@
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.types.*;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
/**
 * An implementation of an Indexer for attribute substrings.
 */
public class SubstringIndexer extends Indexer
public class SubstringIndexer extends ExtensibleIndexer
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The comparator for index keys generated by this class.
   */
  private static final Comparator<byte[]> comparator =
       new AttributeIndex.KeyComparator();
  /**
   * The attribute type for which this instance will
   * generate index keys.
   */
  private AttributeType attributeType;
  private SubstringMatchingRule substringRule;
  private IndexingOptions indexingOptions;
  /**
@@ -72,115 +57,23 @@
  public SubstringIndexer(AttributeType attributeType,
      IndexingOptions indexingOptions)
  {
    this.attributeType = attributeType;
    this.substringRule = attributeType.getSubstringMatchingRule();
    this.indexingOptions = indexingOptions;
  }
  /**
   * Get a string representation of this object.  The returned value is
   * used to name an index created using this object.
   * @return A string representation of this object.
   */
  /** {@inheritDoc} */
  @Override
  public String toString()
  public String getIndexID()
  {
    return attributeType.getNameOrOID() + ".substring";
    // TODO Auto-generated method stub
    throw new RuntimeException();
  }
  /**
   * Get the comparator that must be used to compare index keys
   * generated by this class.
   *
   * @return A byte array comparator.
   */
  /** {@inheritDoc} */
  @Override
  public Comparator<byte[]> getComparator()
  public String getExtensibleIndexID()
  {
    return comparator;
  }
  /**
   * Generate the set of index keys for an entry.
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   */
  @Override
  public void indexEntry(Entry entry, Set<byte[]> keys)
  {
    List<Attribute> attrList =
         entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys);
    }
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that has been replaced.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void replaceEntry(Entry oldEntry, Entry newEntry,
                           Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of index keys to be added and the set of index keys
   * to be deleted for an entry that was modified.
   *
   * @param oldEntry The original entry contents.
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<byte[], Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
  /**
   * Generate the set of substring index keys for an attribute.
   * @param attrList The attribute for which substring keys are required.
   * @param keys The set into which the generated keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Set<byte[]> keys)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        SubstringMatchingRule rule =
            attr.getAttributeType().getSubstringMatchingRule();
        for (AttributeValue value : attr)
        {
          getKeys(rule, value, keys);
        }
      }
    }
    return "substring";
  }
  /**
@@ -191,12 +84,12 @@
   * @param attrValue A byte array containing the normalized attribute value
   * @param keys A set into which the keys will be inserted.
   */
  private void getKeys(SubstringMatchingRule rule, AttributeValue attrValue,
      Set<byte[]> keys)
  @Override
  public void getKeys(AttributeValue attrValue, Set<byte[]> keys)
  { // TODO merge with ExtensibleIndexer.getKeys(attrValue, keys);
    try
    {
      byte[] value = rule.normalizeAttributeValue(attrValue.getValue()).toByteArray();
      byte[] value = substringRule.normalizeAttributeValue(attrValue.getValue()).toByteArray();
      // Example: The value is ABCDE and the substring length is 3.
      // We produce the keys ABC BCD CDE DE E
@@ -233,51 +126,4 @@
    return keyBytes;
  }
  /**
   * Generate the set of index keys for an attribute.
   * @param attrList The attribute to be indexed.
   * @param modifiedKeys The map into which the modified
   * keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
                              Map<byte[], Boolean> modifiedKeys,
                              Boolean insert)
  {
    if (attrList == null) return;
    for (Attribute attr : attrList)
    {
      if (!attr.isVirtual())
      {
        SubstringMatchingRule rule =
            attr.getAttributeType().getSubstringMatchingRule();
        for (AttributeValue value : attr)
        {
          getKeys(rule, value, modifiedKeys, insert);
        }
      }
    }
  }
  /**
   * Decompose an attribute value into a set of substring index keys.
   * The ID of the entry containing this value should be inserted
   * into the list of each of these keys.
   *
   * @param value A byte array containing the normalized attribute value
   * @param modifiedKeys The map into which the modified
   *  keys will be inserted.
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void getKeys(SubstringMatchingRule rule, AttributeValue value,
      Map<byte[], Boolean> modifiedKeys, Boolean insert)
  { // TODO merge with ExtensibleIndexer.getKeys(attrValue, modifiedKeys, insert);
    Set<byte[]> keys = new HashSet<byte[]>();
    getKeys(rule, value, keys);
    ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
  }
}
opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -1350,7 +1350,6 @@
      // initialLength, initial, numberofany, anyLength1, any1,
      // anyLength2, any2, ..., anyLengthn, anyn, finalLength,
      // final
      CollationKey key;
      List<Integer> normalizedList = new ArrayList<Integer>();
      if (subInitial == null)
@@ -1359,16 +1358,7 @@
      }
      else
      {
        key = collator.getCollationKey(subInitial);
        byte[] initialBytes = key.toByteArray();
        // Last 4 bytes are 0s with PRIMARY strength.
        int length = initialBytes.length - 4;
        normalizedList.add(length);
        for (int i = 0; i < length; i++)
        {
          normalizedList.add((int) initialBytes[i]);
        }
        addLengthAndBytes(subInitial, normalizedList);
      }
      List<String> subAny = assertion.getAny();
@@ -1381,14 +1371,7 @@
        normalizedList.add(subAny.size());
        for (String any : subAny)
        {
          key = collator.getCollationKey(any);
          byte[] anyBytes = key.toByteArray();
          int length = anyBytes.length - 4;
          normalizedList.add(length);
          for (int i = 0; i < length; i++)
          {
            normalizedList.add((int) anyBytes[i]);
          }
          addLengthAndBytes(any, normalizedList);
        }
      }
@@ -1399,14 +1382,7 @@
      }
      else
      {
        key = collator.getCollationKey(subFinal);
        byte[] subFinalBytes = key.toByteArray();
        int length = subFinalBytes.length - 4;
        normalizedList.add(length);
        for (int i = 0; i < length; i++)
        {
          normalizedList.add((int) subFinalBytes[i]);
        }
        addLengthAndBytes(subFinal, normalizedList);
      }
      byte[] normalizedBytes = new byte[normalizedList.size()];
@@ -1420,6 +1396,23 @@
    private void addLengthAndBytes(String substring,
        List<Integer> normalizedList)
    {
      CollationKey key = collator.getCollationKey(substring);
      byte[] substrBytes = key.toByteArray();
      // Last 4 bytes are 0s with PRIMARY strength.
      int length = substrBytes.length - 4;
      normalizedList.add(length);
      for (int i = 0; i < length; i++)
      {
        normalizedList.add((int) substrBytes[i]);
      }
    }
    /**
     * {@inheritDoc}
     */
@@ -1566,50 +1559,6 @@
    }
    /**
     * Decomposes an attribute value into a set of substring index keys.
     *
     * @param attValue
     *          The normalized attribute value
     * @param set
     *          A set into which the keys will be inserted.
     */
    private void substringKeys(ByteString attValue, Set<byte[]> keys)
    { // TODO merge with ExtensibleIndexer.getKeys(attrValue, keys);
      // TODO and with AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys();
      String value = attValue.toString();
      int keyLength = subIndexer.getSubstringLength();
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
      {
        int len = Math.min(keyLength, remain);
        keys.add(makeSubstringKey(value, i, len));
      }
    }
    /**
     * Decomposes an attribute value into a set of substring index keys.
     *
     * @param value
     *          The normalized attribute value
     * @param modifiedKeys
     *          The map into which the modified keys will be inserted.
     * @param insert
     *          <code>true</code> if generated keys should be inserted
     *          or <code>false</code> otherwise.
     */
    private void substringKeys(ByteString attValue,
        Map<byte[], Boolean> modifiedKeys, Boolean insert)
    { // TODO merge with ExtensibleIndexer.getKeys(attrValue, modifiedKeys, insert);
      Set<byte[]> keys = new TreeSet<byte[]>();
      substringKeys(attValue, keys);
      ExtensibleIndexer.computeModifiedKeys(modifiedKeys, insert, keys);
    }
    /**
     * Makes a byte array representing a substring index key for one
     * substring of a value.
@@ -2199,21 +2148,16 @@
     * {@inheritDoc}
     */
    @Override
    public void getKeys(AttributeValue value, Set<byte[]> keys)
    {
      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} */
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);