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

sin
31.06.2009 7ddf5a923866031a7034f457715d6a5e4ecbca67
opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.backends.jeb;
import org.opends.messages.Message;
@@ -42,8 +42,12 @@
import org.opends.server.admin.std.server.LocalDBIndexCfg;
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.IndexQueryFactory;
import org.opends.server.config.ConfigException;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.util.ServerConstants.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.util.StaticUtils;
@@ -113,6 +117,12 @@
   */
  Index approximateIndex = null;
  /**
   * The ExtensibleMatchingRuleIndex instance for ExtensibleMatchingRule
   * indexes.
   */
  private  ExtensibleMatchingRuleIndex extensibleIndexes = null;
  private State state;
  private int cursorEntryLimit = 100000;
@@ -238,7 +248,61 @@
                                        env,
                                        entryContainer);
    }
    if (indexConfig.getIndexType().contains(
        LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
    {
      Set<ExtensibleMatchingRule> extensibleRules =
              indexConfig.getIndexExtensibleMatchingRule();
      if(extensibleRules == null || extensibleRules.size() == 0)
      {
        Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
            String.valueOf(attrType), "extensible");
        throw new ConfigException(message);
      }
      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.
      IndexConfig config = new JEIndexConfig(indexConfig.getSubstringLength());
      for(ExtensibleMatchingRule rule:extensibleRules)
      {
        Map<String,Index> indexMap = new HashMap<String,Index>();
        for(ExtensibleIndexer indexer : rule.getIndexers(config))
        {
          String indexerId = indexer.getExtensibleIndexID();
          String indexID =
                  attrType.getNameOrOID()  + "."
                    + indexer.getPreferredIndexName()
                    + "." + indexerId;
          if(!extensibleIndexes.isIndexPresent(indexID))
          {
            //There is no index available for this index id. Create a new index.
            Indexer extensibleIndexer =
                    new JEExtensibleIndexer(attrType,
                                               rule,
                                               indexer);
            String indexName = entryContainer.getDatabasePrefix() + "_"
                                                  + indexID;
            Index extensibleIndex = new Index(indexName,
                                      extensibleIndexer,
                                      state,
                                      indexEntryLimit,
                                      cursorEntryLimit,
                                      false,
                                      env,
                                      entryContainer);
              extensibleIndexes.addIndex(extensibleIndex,indexID);
          }
        extensibleIndexes.addRule(indexID, rule);
        indexMap.put(indexer.getExtensibleIndexID(),
                extensibleIndexes.getIndex(indexID));
      }
      IndexQueryFactory<IndexQuery> factory =
              new IndexQueryFactoryImpl(indexMap);
      extensibleIndexes.addQueryFactory(rule, factory);
      }
    }
    this.indexConfig.addChangeListener(this);
  }
@@ -274,6 +338,14 @@
    {
      approximateIndex.open();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.open();
      }
    }
  }
  /**
@@ -309,6 +381,14 @@
      approximateIndex.close();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.close();
      }
    }
    indexConfig.removeChangeListener(this);
    // The entryContainer is responsible for closing the JE databases.
  }
@@ -388,6 +468,17 @@
      }
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if(!extensibleIndex.addEntry(buffer, entryID,entry))
        {
          success = false;
        }
      }
    }
    return success;
  }
@@ -448,6 +539,17 @@
      }
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if(!extensibleIndex.addEntry(txn, entryID,entry))
        {
          success = false;
        }
      }
    }
    return success;
  }
@@ -488,6 +590,14 @@
    {
      approximateIndex.removeEntry(buffer, entryID, entry);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.removeEntry(buffer, entryID, entry);
      }
    }
  }
  /**
@@ -526,6 +636,14 @@
    {
      approximateIndex.removeEntry(txn, entryID, entry);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.removeEntry(txn, entryID, entry);
      }
    }
  }
  /**
@@ -571,6 +689,14 @@
    {
      approximateIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
      }
    }
  }
  /**
@@ -616,6 +742,14 @@
    {
      approximateIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
      }
    }
  }
  /**
@@ -1249,6 +1383,14 @@
    {
      approximateIndex.closeCursor();
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.closeCursor();
      }
    }
  }
  /**
@@ -1287,6 +1429,14 @@
          approximateIndex.getEntryLimitExceededCount();
    }
     if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        entryLimitExceededCount +=
                extensibleIndex.getEntryLimitExceededCount();
      }
    }
    return entryLimitExceededCount;
  }
@@ -1320,6 +1470,14 @@
    {
      dbList.add(approximateIndex);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        dbList.add(extensibleIndex);
      }
    }
  }
  /**
@@ -1384,6 +1542,18 @@
        return false;
      }
    }
    if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
    {
      Set<ExtensibleMatchingRule> newRules =
              cfg.getIndexExtensibleMatchingRule();
      if (newRules == null || newRules.size() == 0)
      {
        Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
                String.valueOf(attrType), "extensible");
        unacceptableReasons.add(message);
        return false;
      }
    }
    return true;
  }
@@ -1721,6 +1891,171 @@
        }
      }
      if (cfg.getIndexType().contains(
              LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
      {
        Set<ExtensibleMatchingRule> extensibleRules =
            cfg.getIndexExtensibleMatchingRule();
        if(extensibleIndexes == null)
        {
          extensibleIndexes = new ExtensibleMatchingRuleIndex();
        }
        IndexConfig config = new JEIndexConfig(cfg.getSubstringLength());
        for(ExtensibleMatchingRule rule:extensibleRules)
        {
          Map<String,Index> indexMap = new HashMap<String,Index>();
          for(ExtensibleIndexer indexer: rule.getIndexers(config))
          {
            String indexerId = indexer.getExtensibleIndexID();
            String indexID =
                  attrType.getNameOrOID()  + "."
                   + indexer.getPreferredIndexName()
                   + "." + indexerId;
            if(!extensibleIndexes.isIndexPresent(indexID))
            {
              Indexer extensibleIndexer =
                      new JEExtensibleIndexer(attrType,
                                                 rule,
                                                 indexer);
              String indexName =  entryContainer.getDatabasePrefix() + "_"
                      + indexID;
              Index extensibleIndex = new Index(indexName,
                                        extensibleIndexer,
                                        state,
                                        indexEntryLimit,
                                        cursorEntryLimit,
                                        false,
                                        env,
                                        entryContainer);
              extensibleIndexes.addIndex(extensibleIndex,indexID);
            }
            else
            {
              Index extensibleIndex = extensibleIndexes.getIndex(indexID);
              if(extensibleIndex.setIndexEntryLimit(indexEntryLimit))
              {
                adminActionRequired = true;
                Message message =
                      NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
                              extensibleIndex.getName());
                messages.add(message);
              }
              if(indexConfig.getSubstringLength() !=
              cfg.getSubstringLength())
              {
                Indexer extensibleIndexer =
                      new JEExtensibleIndexer(attrType,
                                                 rule,
                                                 indexer);
                extensibleIndex.setIndexer(extensibleIndexer);
              }
            }
            extensibleIndexes.addRule(indexID, rule);
            indexMap.put(indexerId,extensibleIndexes.getIndex(indexID));
          }
          IndexQueryFactory<IndexQuery> factory =
                  new IndexQueryFactoryImpl(indexMap);
          extensibleIndexes.addQueryFactory(rule, factory);
        }
        //Some rules might have been removed from the configuration.
        Set<ExtensibleMatchingRule> deletedRules =
                new HashSet<ExtensibleMatchingRule>();
        for(ExtensibleMatchingRule r:extensibleIndexes.getRules())
        {
          if(!extensibleRules.contains(r))
          {
            deletedRules.add(r);
          }
        }
        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(config))
              {
                String id = attrType.getNameOrOID()  + "."
                 + indexer.getPreferredIndexName()
                 + "." + indexer.getExtensibleIndexID();
                rules.addAll(extensibleIndexes.getRules(id));
                ids.add(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);
                  extensibleIndex = null;
                  extensibleIndexes.deleteIndex(indexID);
                  extensibleIndexes.deleteRule(indexID);
                }
              }
              else
              {
                for(String indexID : ids)
                {
                  extensibleIndexes.deleteRule(rule, indexID);
                }
              }
            }
          }
          catch(DatabaseException de)
          {
            messages.add(
                  Message.raw(StaticUtils.stackTraceToSingleLineString(de)));
            ccr = new ConfigChangeResult(
              DirectoryServer.getServerErrorResultCode(), false, messages);
            return ccr;
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
          }
        }
      }
      else
      {
        if(extensibleIndexes != null)
        {
          entryContainer.exclusiveLock.lock();
          try
          {
            for(Index extensibleIndex:extensibleIndexes.getIndexes())
            {
              entryContainer.deleteDatabase(extensibleIndex);
              extensibleIndex =  null;
            }
            extensibleIndexes.deleteAll();
          }
          catch(DatabaseException de)
          {
            messages.add(
                  Message.raw(StaticUtils.stackTraceToSingleLineString(de)));
            ccr = new ConfigChangeResult(
              DirectoryServer.getServerErrorResultCode(), false, messages);
            return ccr;
          }
          finally
          {
            entryContainer.exclusiveLock.unlock();
          }
        }
      }
      indexConfig = cfg;
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
@@ -1770,6 +2105,14 @@
    {
      approximateIndex.setTrusted(txn, trusted);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.setTrusted(txn, trusted);
      }
    }
  }
  /**
@@ -1798,11 +2141,22 @@
      return false;
    }
    if (approximateIndex != null && approximateIndex.isTrusted())
    if (approximateIndex != null && !approximateIndex.isTrusted())
    {
      return false;
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        if(extensibleIndex !=null && !extensibleIndex.isTrusted())
        {
          return false;
        }
      }
    }
    return true;
  }
@@ -1837,6 +2191,14 @@
    {
      approximateIndex.setRebuildStatus(rebuildRunning);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        extensibleIndex.setRebuildStatus(rebuildRunning);
      }
    }
  }
  /**
@@ -1900,6 +2262,21 @@
  }
  /**
   * Return the mapping of  extensible index types and indexes.
   *
   * @return The Map of extensible index types and indexes.
   */
  public Map<String,Collection<Index>> getExtensibleIndexes()
  {
    if(extensibleIndexes == null)
    {
      return Collections.emptyMap();
    }
    return extensibleIndexes.getIndexMap();
  }
  /**
   * Retrieves all the indexes used by this attribute index.
   *
   * @return A collection of all indexes in use by this attribute
@@ -1933,6 +2310,332 @@
      indexes.add(approximateIndex);
    }
    if(extensibleIndexes!=null)
    {
      for(Index extensibleIndex:extensibleIndexes.getIndexes())
      {
        indexes.add(extensibleIndex);
      }
    }
    return indexes;
  }
}
  /**
   * Retrieve the entry IDs that might match an extensible filter.
   *
   * @param extensibleFilter The extensible filter.
   * @param debugBuffer If not null, a diagnostic string will be written
   *                     which will help determine how the indexes contributed
   *                     to this search.
   * @return The candidate entry IDs that might contain the filter
   *         assertion value.
   */
  public EntryIDSet evaluateExtensibleFilter(SearchFilter extensibleFilter,
                                              StringBuilder debugBuffer)
  {
    //Get the Matching Rule OID of the filter.
    String nOID  = extensibleFilter.getMatchingRuleID();
    ExtensibleMatchingRule rule =
            DirectoryServer.getExtensibleMatchingRule(nOID);
    IndexQueryFactory<IndexQuery> factory = null;
    if(extensibleIndexes == null
            || (factory = extensibleIndexes.getQueryFactory(rule))==null)
    {
      // There is no index on this matching rule.
      return IndexQuery.createNullIndexQuery().evaluate();
    }
    try
    {
      if(debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        IndexConfig config =
                new JEIndexConfig(indexConfig.getSubstringLength());
        for(ExtensibleIndexer indexer :  rule.getIndexers(config))
        {
          String indexerID = indexer.getExtensibleIndexID();
          String indexName = indexer.getPreferredIndexName();
          String indexID = " "
                         + extensibleFilter.getAttributeType().getNameOrOID()
                         + "." + indexName
                         + "." +indexerID;
          debugBuffer.append(indexID);
        }
        debugBuffer.append("]");
      }
      ByteString assertionValue =
              extensibleFilter.getAssertionValue().getValue();
      IndexQuery expression = rule.createIndexQuery(assertionValue, factory);
      return expression.evaluate();
    }
    catch (DirectoryException e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      return IndexQuery.createNullIndexQuery().evaluate();
    }
  }
  /**
   * This class manages all the configured extensible matching rules and
   * their corresponding indexes.
   */
  private class ExtensibleMatchingRuleIndex
  {
    /**
      * 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()
    {
      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)
      {
        return Collections.emptySet();
      }
      else
      {
        return Collections.unmodifiableSet(id2RulesMap.get(indexID));
      }
    }
    /**
     * 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.size() == 0)
      {
        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);
      rule2FactoryMap.remove(rules);
      rules.clear();
      id2RulesMap.remove(indexID);
    }
    /**
     * Deletes all references to matching rules and the indexes.
     */
    private void deleteAll()
    {
      id2IndexMap.clear();
      id2RulesMap.clear();
      rule2FactoryMap.clear();
    }
  }
  /**
   * This class extends the IndexConfig for JE Backend.
   */
  private class JEIndexConfig extends IndexConfig
  {
    //The length of the substring index.
    private int substringLength;
    /**
     * Creates a new JEIndexConfig instance.
     * @param substringLength The length of the substring.
     */
    private JEIndexConfig(int substringLength)
    {
      this.substringLength = substringLength;
    }
    /**
     * Returns the length of the substring.
     * @return the length of the substring.
     */
   public int getSubstringLength()
   {
     return substringLength;
   }
  }
}