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

Nicolas Capponi
06.50.2014 4cb8262e95fde77e6a0d2c84f1aa118e3b1ee850
opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -45,8 +45,7 @@
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn.IndexType;
import org.opends.server.admin.std.server.LocalDBIndexCfg;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.MatchingRule;
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.monitors.DatabaseEnvironmentMonitor;
import org.opends.server.types.*;
@@ -112,14 +111,14 @@
  private final Map<String, Index> nameToIndexes;
  private final IndexQueryFactory<IndexQuery> indexQueryFactory;
  /**
   * The ExtensibleMatchingRuleIndex instance for ExtensibleMatchingRule
   * indexes.
   */
  private ExtensibleMatchingRuleIndex extensibleIndexes;
  private int cursorEntryLimit = 100000;
  /**
   * The mapping from extensible index types (e.g. "substring" or "shared") to list of indexes.
   */
  private Map<String, Collection<Index>> extensibleIndexesMapping;
  /**
   * Create a new attribute index object.
   *
   * @param entryContainer The entryContainer of this attribute index.
@@ -179,7 +178,6 @@
      nameToIndexes.put(IndexType.APPROXIMATE.toString(), approximateIndex);
    }
    indexQueryFactory = new IndexQueryFactoryImpl(nameToIndexes, config);
    if (indexConfig.getIndexType().contains(IndexType.EXTENSIBLE))
    {
@@ -188,39 +186,36 @@
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, "extensible"));
      }
      extensibleIndexes = new ExtensibleMatchingRuleIndex();
      // Iterate through the Set and create the index only if necessary.
      // Collation equality and Ordering matching rules share the same
      // indexer and index.
      // A Collation substring matching rule is treated differently
      // as it uses a separate indexer and index.
      for(String ruleName:extensibleRules)
      for (final String ruleName : extensibleRules)
      {
        ExtensibleMatchingRule rule = DirectoryServer.getExtensibleMatchingRule(toLowerCase(ruleName));
        MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
        if(rule == null)
        {
          logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
          continue;
        }
        Map<String,Index> indexMap = new HashMap<String,Index>();
        for (ExtensibleIndexer indexer : rule.getIndexers())
        for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
        {
          String indexID = attrType.getNameOrOID() + "." + indexer.getIndexID();
          if(!extensibleIndexes.isIndexPresent(indexID))
          final String indexId = indexer.getIndexID();
          if (!nameToIndexes.containsKey(indexId))
          {
            //There is no index available for this index id. Create a new index.
            String indexName = entryContainer.getDatabasePrefix() + "_" + indexID;
            Index extIndex = newExtensibleIndex(indexName, attrType, indexEntryLimit, indexer);
            extensibleIndexes.addIndex(extIndex, indexID);
            final String indexName = name + "." + indexId;
            final Index extIndex = newExtensibleIndex(indexName, attrType, indexEntryLimit, indexer);
            nameToIndexes.put(indexId, extIndex);
          }
          extensibleIndexes.addRule(indexID, rule);
          indexMap.put(indexer.getExtensibleIndexID(), extensibleIndexes.getIndex(indexID));
        }
        IndexQueryFactory<IndexQuery> factory = new IndexQueryFactoryImpl(indexMap, config);
        extensibleIndexes.addQueryFactory(rule, factory);
      }
    }
    indexQueryFactory = new IndexQueryFactoryImpl(nameToIndexes, config);
    extensibleIndexesMapping = computeExtensibleIndexesMapping();
  }
  private Index newIndex(String indexName, int indexEntryLimit, Indexer indexer)
@@ -230,20 +225,20 @@
  }
  private Index buildExtIndex(String name, AttributeType attrType,
      int indexEntryLimit, MatchingRule rule, ExtensibleIndexer extIndexer) throws ConfigException
      int indexEntryLimit, MatchingRule rule, org.forgerock.opendj.ldap.spi.Indexer extIndexer) throws ConfigException
  {
    if (rule == null)
    {
      throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
          attrType, extIndexer.getExtensibleIndexID()));
          attrType, extIndexer.getIndexID()));
    }
    final String indexName = name + "." + extIndexer.getExtensibleIndexID();
    final String indexName = name + "." + extIndexer.getIndexID();
    return newExtensibleIndex(indexName, attrType, indexEntryLimit, extIndexer);
  }
  private Index newExtensibleIndex(String indexName, AttributeType attrType,
      int indexEntryLimit, ExtensibleIndexer extIndexer)
      int indexEntryLimit, org.forgerock.opendj.ldap.spi.Indexer extIndexer)
  {
    JEExtensibleIndexer indexer = new JEExtensibleIndexer(attrType, extIndexer);
    return newIndex(indexName, indexEntryLimit, indexer);
@@ -261,14 +256,6 @@
    {
      index.open();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.open();
      }
    }
    indexConfig.addChangeListener(this);
  }
@@ -281,11 +268,6 @@
  public void close() throws DatabaseException
  {
    Utils.closeSilently(nameToIndexes.values());
    if(extensibleIndexes!=null)
    {
      Utils.closeSilently(extensibleIndexes.getIndexes());
    }
    indexConfig.removeChangeListener(this);
    // The entryContainer is responsible for closing the JE databases.
  }
@@ -342,18 +324,6 @@
        success = false;
      }
    }
    if (extensibleIndexes != null)
    {
      for (Index index : extensibleIndexes.getIndexes())
      {
        if (!index.addEntry(buffer, entryID, entry, options))
        {
          success = false;
        }
      }
    }
    return success;
  }
@@ -381,18 +351,6 @@
        success = false;
      }
    }
    if (extensibleIndexes != null)
    {
      for (Index index : extensibleIndexes.getIndexes())
      {
        if (!index.addEntry(txn, entryID, entry, options))
        {
          success = false;
        }
      }
    }
    return success;
  }
@@ -413,14 +371,6 @@
    {
      index.removeEntry(buffer, entryID, entry, options);
    }
    if (extensibleIndexes != null)
    {
      for (Index index : extensibleIndexes.getIndexes())
      {
        index.removeEntry(buffer, entryID, entry, options);
      }
    }
  }
  /**
@@ -440,14 +390,6 @@
    {
      index.removeEntry(txn, entryID, entry, options);
    }
    if (extensibleIndexes != null)
    {
      for (Index index : extensibleIndexes.getIndexes())
      {
        index.removeEntry(txn, entryID, entry, options);
      }
    }
  }
  /**
@@ -474,14 +416,6 @@
    {
      index.modifyEntry(txn, entryID, oldEntry, newEntry, mods, options);
    }
    if (extensibleIndexes != null)
    {
      for (Index index : extensibleIndexes.getIndexes())
      {
        index.modifyEntry(txn, entryID, oldEntry, newEntry, mods, options);
      }
    }
  }
  /**
@@ -508,14 +442,6 @@
    {
      index.modifyEntry(buffer, entryID, oldEntry, newEntry, mods, options);
    }
    if(extensibleIndexes!=null)
    {
      for (Index index : extensibleIndexes.getIndexes())
      {
        index.modifyEntry(buffer, entryID, oldEntry, newEntry, mods, options);
      }
    }
  }
  /**
@@ -916,14 +842,6 @@
    {
      index.closeCursor();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.closeCursor();
      }
    }
  }
  /**
@@ -940,14 +858,6 @@
    {
      entryLimitExceededCount += index.getEntryLimitExceededCount();
    }
    if (extensibleIndexes != null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        entryLimitExceededCount += extensibleIndex.getEntryLimitExceededCount();
      }
    }
    return entryLimitExceededCount;
  }
@@ -958,11 +868,6 @@
  public void listDatabases(List<DatabaseContainer> dbList)
  {
    dbList.addAll(nameToIndexes.values());
    if(extensibleIndexes!=null)
    {
      dbList.addAll(extensibleIndexes.getIndexes());
    }
  }
  /**
@@ -1036,12 +941,11 @@
      AttributeType attrType = cfg.getAttribute();
      String name = entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID();
      final int indexEntryLimit = cfg.getIndexEntryLimit();
      final JEIndexConfig config = new JEIndexConfig(cfg.getSubstringLength());
      Index presenceIndex = nameToIndexes.get(IndexType.PRESENCE.toString());
      if (cfg.getIndexType().contains(IndexType.PRESENCE))
      {
        if(presenceIndex == null)
        if (presenceIndex == null)
        {
          Indexer presenceIndexer = new PresenceIndexer(attrType);
          presenceIndex = newIndex(name + ".presence", indexEntryLimit, presenceIndexer);
@@ -1051,7 +955,7 @@
        else
        {
          // already exists. Just update index entry limit.
          if(presenceIndex.setIndexEntryLimit(indexEntryLimit))
          if (presenceIndex.setIndexEntryLimit(indexEntryLimit))
          {
            adminActionRequired.set(true);
            messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(presenceIndex.getName()));
@@ -1074,123 +978,55 @@
      if (cfg.getIndexType().contains(IndexType.EXTENSIBLE))
      {
        Set<String> extensibleRules = cfg.getIndexExtensibleMatchingRule();
        Set<ExtensibleMatchingRule> validRules = new HashSet<ExtensibleMatchingRule>();
        if(extensibleIndexes == null)
        final Set<String> extensibleRules = cfg.getIndexExtensibleMatchingRule();
        final Set<MatchingRule> validRules = new HashSet<MatchingRule>();
        final Set<String> validIndexIds = new HashSet<String>();
        for (String ruleName: extensibleRules)
        {
          extensibleIndexes = new ExtensibleMatchingRuleIndex();
        }
        for(String ruleName:extensibleRules)
        {
          ExtensibleMatchingRule rule = DirectoryServer.getExtensibleMatchingRule(toLowerCase(ruleName));
          if(rule == null)
          MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
          if (rule == null)
          {
            logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
            continue;
          }
          validRules.add(rule);
          Map<String,Index> indexMap = new HashMap<String,Index>();
          for (ExtensibleIndexer indexer : rule.getIndexers())
          for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
          {
            String indexID = attrType.getNameOrOID() + "." + indexer.getIndexID();
            if(!extensibleIndexes.isIndexPresent(indexID))
            String indexId = indexer.getIndexID();
            validIndexIds.add(indexId);
            if (!nameToIndexes.containsKey(indexId))
            {
              String indexName =  entryContainer.getDatabasePrefix() + "_" + indexID;
              String indexName =  name + "." + indexId;
              Index extIndex = newExtensibleIndex(indexName, attrType, indexEntryLimit, indexer);
              extensibleIndexes.addIndex(extIndex,indexID);
              nameToIndexes.put(indexId, extIndex);
              openIndex(extIndex, adminActionRequired, messages);
            }
            else
            {
              Index extensibleIndex = extensibleIndexes.getIndex(indexID);
              if(extensibleIndex.setIndexEntryLimit(indexEntryLimit))
              Index extensibleIndex = nameToIndexes.get(indexId);
              if (extensibleIndex.setIndexEntryLimit(indexEntryLimit))
              {
                adminActionRequired.set(true);
                messages.add(NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(extensibleIndex.getName()));
              }
              if (indexConfig.getSubstringLength() != cfg.getSubstringLength())
              {
                extensibleIndex.setIndexer(
                    new JEExtensibleIndexer(attrType, indexer));
              }
            }
            extensibleIndexes.addRule(indexID, rule);
            indexMap.put(indexer.getExtensibleIndexID(), extensibleIndexes.getIndex(indexID));
          }
          IndexQueryFactory<IndexQuery> factory = new IndexQueryFactoryImpl(indexMap, config);
          extensibleIndexes.addQueryFactory(rule, factory);
        }
        //Some rules might have been removed from the configuration.
        Set<ExtensibleMatchingRule> deletedRules =
            new HashSet<ExtensibleMatchingRule>(extensibleIndexes.getRules());
        deletedRules.removeAll(validRules);
        if(deletedRules.size() > 0)
        {
          entryContainer.exclusiveLock.lock();
          try
          {
            for(ExtensibleMatchingRule rule:deletedRules)
            {
              Set<ExtensibleMatchingRule> rules = new HashSet<ExtensibleMatchingRule>();
              List<String> ids = new ArrayList<String>();
              for (ExtensibleIndexer indexer : rule.getIndexers())
              {
                String id = attrType.getNameOrOID()  + "." + indexer.getIndexID();
                ids.add(id);
                rules.addAll(extensibleIndexes.getRules(id));
              }
              if(rules.isEmpty())
              {
                //Rule has been already deleted.
                continue;
              }
              //If all the rules are part of the deletedRules, delete this index
              if(deletedRules.containsAll(rules))
              {
                //it is safe to delete this index as it is not shared.
                for(String indexID : ids)
                {
                  Index extensibleIndex = extensibleIndexes.getIndex(indexID);
                  entryContainer.deleteDatabase(extensibleIndex);
                  extensibleIndexes.deleteIndex(indexID);
                  extensibleIndexes.deleteRule(indexID);
                }
              }
              else
              {
                for(String indexID : ids)
                {
                  extensibleIndexes.deleteRule(rule, indexID);
                }
                extensibleIndex.setIndexer(new JEExtensibleIndexer(attrType, indexer));
              }
            }
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
          }
        }
        removeIndexesForExtensibleMatchingRules(validRules, validIndexIds);
      }
      else
      {
        if(extensibleIndexes != null)
        {
          entryContainer.exclusiveLock.lock();
          try
          {
            for(Index extensibleIndex:extensibleIndexes.getIndexes())
            {
              entryContainer.deleteDatabase(extensibleIndex);
            }
            extensibleIndexes.deleteAll();
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
          }
        }
        final Set<MatchingRule> validRules = Collections.emptySet();
        final Set<String> validIndexIds = Collections.emptySet();
        removeIndexesForExtensibleMatchingRules(validRules, validIndexIds);
      }
      extensibleIndexesMapping = computeExtensibleIndexesMapping();
      indexConfig = cfg;
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired.get(), messages);
@@ -1203,6 +1039,60 @@
    }
  }
  /** Remove indexes which do not correspond to valid rules. */
  private void removeIndexesForExtensibleMatchingRules(Set<MatchingRule> validRules, Set<String> validIndexIds)
  {
    final Set<MatchingRule> rulesToDelete = getCurrentExtensibleMatchingRules();
    rulesToDelete.removeAll(validRules);
    if (!rulesToDelete.isEmpty())
    {
      entryContainer.exclusiveLock.lock();
      try
      {
        for (MatchingRule rule: rulesToDelete)
        {
          final List<String> indexIdsToRemove = new ArrayList<String>();
          for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
          {
            final String indexId = indexer.getIndexID();
            if (!validIndexIds.contains(indexId))
            {
              indexIdsToRemove.add(indexId);
            }
          }
          // Delete indexes which are not used
          for (String indexId : indexIdsToRemove)
          {
            Index index = nameToIndexes.get(indexId);
            if (index != null)
            {
              entryContainer.deleteDatabase(index);
              nameToIndexes.remove(index);
            }
          }
        }
      }
      finally
      {
        entryContainer.exclusiveLock.unlock();
      }
    }
  }
  private Set<MatchingRule> getCurrentExtensibleMatchingRules()
  {
    final Set<MatchingRule> rules = new HashSet<MatchingRule>();
    for (String ruleName : indexConfig.getIndexExtensibleMatchingRule())
    {
        final MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
        if (rule != null)
        {
          rules.add(rule);
        }
    }
    return rules;
  }
  private void applyChangeToIndex(LocalDBIndexCfg cfg, AttributeType attrType,
      String name, IndexType indexType, ExtensibleIndexer indexer,
      AtomicBoolean adminActionRequired, ArrayList<LocalizableMessage> messages)
@@ -1255,7 +1145,7 @@
      int indexEntryLimit, ExtensibleIndexer indexer,
      AtomicBoolean adminActionRequired, ArrayList<LocalizableMessage> messages)
  {
    final String indexName = name + "." + indexer.getExtensibleIndexID();
    final String indexName = name + "." + indexer.getIndexID();
    Index index = newExtensibleIndex(indexName, attrType, indexEntryLimit, indexer);
    return openIndex(index, adminActionRequired, messages);
  }
@@ -1287,14 +1177,6 @@
    {
      index.setTrusted(txn, trusted);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.setTrusted(txn, trusted);
      }
    }
  }
  /**
@@ -1310,18 +1192,6 @@
        return false;
      }
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if (!extensibleIndex.isTrusted())
        {
          return false;
        }
      }
    }
    return true;
  }
@@ -1336,14 +1206,6 @@
    {
      index.setRebuildStatus(rebuildRunning);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.setRebuildStatus(rebuildRunning);
      }
    }
  }
  /**
@@ -1404,19 +1266,48 @@
  }
  /**
   * Return the mapping of  extensible index types and indexes.
   * Return the mapping of extensible index types and indexes.
   *
   * @return The Map of extensible index types and indexes.
   * @return The map containing entries (extensible index type, list of indexes)
   */
  public Map<String,Collection<Index>> getExtensibleIndexes()
  public Map<String, Collection<Index>> getExtensibleIndexes()
  {
    if (extensibleIndexes != null)
    {
      return extensibleIndexes.getIndexMap();
    }
    return Collections.emptyMap();
    return extensibleIndexesMapping;
  }
  private Map<String, Collection<Index>> computeExtensibleIndexesMapping()
  {
    final Collection<Index> substring = new ArrayList<Index>();
    final Collection<Index> shared = new ArrayList<Index>();
    for (Map.Entry<String, Index> entry : nameToIndexes.entrySet())
    {
      final String indexId = entry.getKey();
      if (isDefaultIndex(indexId)) {
        continue;
      }
      if (indexId.endsWith(EXTENSIBLE_INDEXER_ID_SUBSTRING))
      {
        substring.add(entry.getValue());
      }
      else
      {
        shared.add(entry.getValue());
      }
    }
    final Map<String, Collection<Index>> indexMap = new HashMap<String,Collection<Index>>();
    indexMap.put(EXTENSIBLE_INDEXER_ID_SUBSTRING, substring);
    indexMap.put(EXTENSIBLE_INDEXER_ID_SHARED, shared);
    return Collections.unmodifiableMap(indexMap);
  }
  private boolean isDefaultIndex(String indexId)
  {
    return indexId.equals(IndexType.EQUALITY.toString())
        || indexId.equals(IndexType.PRESENCE.toString())
        || indexId.equals(IndexType.SUBSTRING.toString())
        || indexId.equals(IndexType.ORDERING.toString())
        || indexId.equals(IndexType.APPROXIMATE.toString());
  }
  /**
   * Retrieves all the indexes used by this attribute index.
@@ -1425,17 +1316,9 @@
   * index.
   */
  public Collection<Index> getAllIndexes() {
    LinkedHashSet<Index> indexes = new LinkedHashSet<Index>();
    indexes.addAll(nameToIndexes.values());
    if(extensibleIndexes!=null)
    {
      indexes.addAll(extensibleIndexes.getIndexes());
    }
    return indexes;
    return new LinkedHashSet<Index>(nameToIndexes.values());
  }
  /**
   * Retrieve the entry IDs that might match an extensible filter.
   *
@@ -1468,11 +1351,9 @@
      return evaluateEqualityFilter(filter, debugBuffer, monitor);
    }
    ExtensibleMatchingRule rule = DirectoryServer.getExtensibleMatchingRule(matchRuleOID);
    IndexQueryFactory<IndexQuery> factory = null;
    if (extensibleIndexes == null || (factory = extensibleIndexes.getQueryFactory(rule)) == null)
    MatchingRule rule = DirectoryServer.getMatchingRule(matchRuleOID);
    if (!ruleHasAtLeasOneIndex(rule))
    {
      // There is no index on this matching rule.
      if (monitor.isFilterUseEnabled())
      {
        monitor.updateStats(filter, INFO_JEB_INDEX_FILTER_MATCHING_RULE_NOT_INDEXED.get(
@@ -1486,17 +1367,17 @@
      if (debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        for (ExtensibleIndexer indexer : rule.getIndexers())
        for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
        {
          debugBuffer.append(" ")
                     .append(filter.getAttributeType().getNameOrOID())
                     .append(".")
                     .append(indexer.getIndexID());
            debugBuffer.append(" ")
              .append(filter.getAttributeType().getNameOrOID())
              .append(".")
              .append(indexer.getIndexID());
        }
        debugBuffer.append("]");
      }
      ByteString assertionValue = filter.getAssertionValue();
      IndexQuery indexQuery = rule.createIndexQuery(assertionValue, factory);
      final IndexQuery indexQuery = rule.getAssertion(filter.getAssertionValue()).createIndexQuery(indexQueryFactory);
      LocalizableMessageBuilder debugMessage = monitor.isFilterUseEnabled() ? new LocalizableMessageBuilder() : null;
      EntryIDSet results = indexQuery.evaluate(debugMessage);
      if (monitor.isFilterUseEnabled())
@@ -1519,232 +1400,18 @@
    }
  }
  /**
   * This class manages all the configured extensible matching rules and
   * their corresponding indexes.
   */
  private static class ExtensibleMatchingRuleIndex
  private boolean ruleHasAtLeasOneIndex(MatchingRule rule)
  {
    /**
      * The mapping of index ID and Index database.
      */
    private final Map<String,Index> id2IndexMap;
    /**
     * The mapping of Index ID and Set the matching rules.
     */
    private final Map<String,Set<ExtensibleMatchingRule>> id2RulesMap;
    /**
     * The Map of configured ExtensibleMatchingRule and the corresponding
     * IndexQueryFactory.
     */
    private final Map<ExtensibleMatchingRule,
            IndexQueryFactory<IndexQuery>> rule2FactoryMap;
    /**
     * Creates a new instance of ExtensibleMatchingRuleIndex.
     */
    private ExtensibleMatchingRuleIndex()
    boolean ruleHasAtLeastOneIndex = false;
    for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
    {
      id2IndexMap = new HashMap<String,Index>();
      id2RulesMap = new HashMap<String,Set<ExtensibleMatchingRule>>();
      rule2FactoryMap = new HashMap<ExtensibleMatchingRule,
              IndexQueryFactory<IndexQuery>>();
    }
    /**
     * Returns all configured ExtensibleMatchingRule instances.
     * @return A Set  of extensible matching rules.
     */
    private Set<ExtensibleMatchingRule> getRules()
    {
      return rule2FactoryMap.keySet();
    }
    /**
     * Returns  ExtensibleMatchingRule instances for an index.
     * @param indexID The index ID of an extensible matching rule index.
     * @return A Set of extensible matching rules corresponding to
     *                 an index ID.
     */
    private Set<ExtensibleMatchingRule> getRules(String indexID)
    {
      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexID);
      if (rules != null)
      if (nameToIndexes.containsKey(indexer.getIndexID()))
      {
        return Collections.unmodifiableSet(rules);
        ruleHasAtLeastOneIndex = true;
        break;
      }
      return Collections.emptySet();
    }
    /**
     * Returns whether an index is present or not.
     * @param indexID The index ID of an extensible matching rule index.
     * @return True if an index is present. False if there is no matching index.
     */
    private boolean isIndexPresent(String indexID)
    {
      return id2IndexMap.containsKey(indexID);
    }
    /**
     * Returns the index corresponding to an index ID.
     * @param indexID The ID of an index.
     * @return The extensible rule index corresponding to the index ID.
     */
    private Index getIndex(String indexID)
    {
      return id2IndexMap.get(indexID);
    }
    /**
     * Adds a new matching Rule and the name of the associated index.
     * @indexName Name of the index.
     * @rule An ExtensibleMatchingRule instance that needs to be indexed.
     */
    private void addRule(String indexName,ExtensibleMatchingRule rule)
    {
      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexName);
      if(rules == null)
      {
        rules = new HashSet<ExtensibleMatchingRule>();
        id2RulesMap.put(indexName, rules);
      }
      rules.add(rule);
    }
    /**
     * Adds a new Index and its name.
     * @param index The extensible matching rule index.
     * @indexName The name of the index.
     */
    private void addIndex(Index index,String indexName)
    {
      id2IndexMap.put(indexName, index);
    }
    /**
     * Returns all the configured extensible indexes.
     * @return All the available extensible matching rule indexes.
     */
    private Collection<Index> getIndexes()
    {
      return Collections.unmodifiableCollection(id2IndexMap.values());
    }
    /**
     * Returns a map of all the configured extensible indexes and their types.
     * @return A map of all the available extensible matching rule indexes
     *             and their types.
     */
    private Map<String,Collection<Index>> getIndexMap()
    {
      if(id2IndexMap.isEmpty())
      {
        return Collections.emptyMap();
      }
      Collection<Index> substring = new ArrayList<Index>();
      Collection<Index> shared = new ArrayList<Index>();
      for(Map.Entry<String,Index> entry :  id2IndexMap.entrySet())
      {
        String indexID = entry.getKey();
        if(indexID.endsWith(EXTENSIBLE_INDEXER_ID_SUBSTRING))
        {
          substring.add(entry.getValue());
        }
        else
        {
          shared.add(entry.getValue());
        }
      }
      Map<String,Collection<Index>> indexMap =
              new HashMap<String,Collection<Index>>();
      indexMap.put(EXTENSIBLE_INDEXER_ID_SUBSTRING, substring);
      indexMap.put(EXTENSIBLE_INDEXER_ID_SHARED, shared);
      return Collections.unmodifiableMap(indexMap);
    }
    /**
     * Deletes an index corresponding to the index ID.
     * @param indexID Name of the index.
     */
    private void deleteIndex(String indexID)
    {
      id2IndexMap.remove(indexID);
    }
    /**
     * Deletes an extensible matching rule from the list of available rules.
     * @param rule The ExtensibleMatchingRule that needs to be removed.
     * @param indexID The name of the index corresponding to the rule.
     */
    private void deleteRule(ExtensibleMatchingRule rule,String indexID)
    {
      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexID);
      rules.remove(rule);
      if(rules.isEmpty())
      {
        id2RulesMap.remove(indexID);
      }
      rule2FactoryMap.remove(rule);
    }
    /**
     * Adds an ExtensibleMatchingRule and its corresponding IndexQueryFactory.
     * @param rule An ExtensibleMatchingRule that needs to be added.
     * @param query A query factory matching the rule.
     */
    private void addQueryFactory(ExtensibleMatchingRule rule,
            IndexQueryFactory<IndexQuery> query)
    {
      rule2FactoryMap.put(rule, query);
    }
    /**
     * Returns the query factory associated with the rule.
     * @param rule An ExtensibleMatchingRule that needs to be searched.
     * @return An IndexQueryFactory corresponding to the matching rule.
     */
    private IndexQueryFactory<IndexQuery> getQueryFactory(
            ExtensibleMatchingRule rule)
    {
      return rule2FactoryMap.get(rule);
    }
    /**
     * Deletes  extensible matching rules from the list of available rules.
     * @param indexID The name of the index corresponding to the rules.
     */
    private void deleteRule(String indexID)
    {
      Set<ExtensibleMatchingRule> rules  = id2RulesMap.get(indexID);
      for (ExtensibleMatchingRule rule : rules)
      {
        rule2FactoryMap.remove(rule);
      }
      rules.clear();
      id2RulesMap.remove(indexID);
    }
    /**
     * Deletes all references to matching rules and the indexes.
     */
    private void deleteAll()
    {
      id2IndexMap.clear();
      id2RulesMap.clear();
      rule2FactoryMap.clear();
    }
    return ruleHasAtLeastOneIndex;
  }
  /**