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

boli
27.06.2007 05875933ae6929bc8e53f366ce116f2fc431fd46
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -59,6 +59,7 @@
import static org.opends.server.util.ServerConstants.*;
import org.opends.server.admin.std.server.JEBackendCfg;
import org.opends.server.admin.std.server.JEIndexCfg;
import org.opends.server.admin.std.server.VLVJEIndexCfg;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
@@ -70,9 +71,7 @@
 * the guts of the backend API methods for LDAP operations.
 */
public class EntryContainer
    implements ConfigurationChangeListener<JEBackendCfg>,
    ConfigurationAddListener<JEIndexCfg>,
    ConfigurationDeleteListener<JEIndexCfg>
    implements ConfigurationChangeListener<JEBackendCfg>
{
  /**
   * The tracer object for the debug logger.
@@ -116,6 +115,16 @@
  public static final String ATTR_DEBUG_SEARCH_INDEX = "debugsearchindex";
  /**
   * The attribute index configuration manager.
   */
  public AttributeJEIndexCfgManager attributeJEIndexCfgManager;
  /**
   * The vlv index configuration manager.
   */
  public VLVJEIndexCfgManager vlvJEIndexCfgManager;
  /**
   * The backend to which this entry entryContainer belongs.
   */
  private Backend backend;
@@ -176,6 +185,11 @@
  private HashMap<AttributeType, AttributeIndex> attrIndexMap;
  /**
   * The set of VLV indexes.
   */
  private HashMap<String, VLVIndex> vlvIndexMap;
  /**
   * Cached value from config so they don't have to be retrieved per operation.
   */
  private int deadlockRetryLimit;
@@ -188,6 +202,196 @@
  private String databasePrefix;
  /**
   * This class is responsible for managing the configuraiton for attribute
   * indexes used within this entry container.
   */
  public class AttributeJEIndexCfgManager implements
      ConfigurationAddListener<JEIndexCfg>,
      ConfigurationDeleteListener<JEIndexCfg>
  {
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationAddAcceptable(JEIndexCfg cfg,
                                               List<String> unacceptableReasons)
    {
      // TODO: validate more before returning true?
      return true;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationAdd(JEIndexCfg cfg)
    {
      ConfigChangeResult ccr;
      boolean adminActionRequired = false;
      ArrayList<String> messages = new ArrayList<String>();
      try
      {
        AttributeIndex index =
            new AttributeIndex(cfg, state, env, EntryContainer.this);
        index.open();
        attrIndexMap.put(cfg.getIndexAttribute(), index);
      }
      catch(Exception e)
      {
        messages.add(StaticUtils.stackTraceToSingleLineString(e));
        ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                     adminActionRequired,
                                     messages);
        return ccr;
      }
      adminActionRequired = true;
      int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD;
      messages.add(getMessage(msgID, cfg.getIndexAttribute().getNameOrOID()));
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                    messages);
    }
    /**
     * {@inheritDoc}
     */
    public synchronized boolean isConfigurationDeleteAcceptable(
        JEIndexCfg cfg, List<String> unacceptableReasons)
    {
      // TODO: validate more before returning true?
      return true;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationDelete(JEIndexCfg cfg)
    {
      ConfigChangeResult ccr;
      boolean adminActionRequired = false;
      ArrayList<String> messages = new ArrayList<String>();
      exclusiveLock.lock();
      try
      {
        AttributeIndex index = attrIndexMap.get(cfg.getIndexAttribute());
        deleteAttributeIndex(index);
        attrIndexMap.remove(cfg.getIndexAttribute());
      }
      catch(DatabaseException de)
      {
        messages.add(StaticUtils.stackTraceToSingleLineString(de));
        ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                     adminActionRequired,
                                     messages);
        return ccr;
      }
      finally
      {
        exclusiveLock.unlock();
      }
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                    messages);
    }
  }
  /**
   * This class is responsible for managing the configuraiton for VLV indexes
   * used within this entry container.
   */
  public class VLVJEIndexCfgManager implements
      ConfigurationAddListener<VLVJEIndexCfg>,
      ConfigurationDeleteListener<VLVJEIndexCfg>
  {
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationAddAcceptable(
        VLVJEIndexCfg cfg, List<String> unacceptableReasons)
    {
      // TODO: validate more before returning true?
      return true;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationAdd(VLVJEIndexCfg cfg)
    {
      ConfigChangeResult ccr;
      boolean adminActionRequired = false;
      ArrayList<String> messages = new ArrayList<String>();
      try
      {
        VLVIndex vlvIndex = new VLVIndex(cfg, state, env, EntryContainer.this);
        vlvIndex.open();
        vlvIndexMap.put(cfg.getVLVIndexName().toLowerCase(), vlvIndex);
      }
      catch(Exception e)
      {
        messages.add(StaticUtils.stackTraceToSingleLineString(e));
        ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                     adminActionRequired,
                                     messages);
        return ccr;
      }
      adminActionRequired = true;
      int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD;
      messages.add(getMessage(msgID, cfg.getVLVIndexName()));
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                    messages);
    }
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationDeleteAcceptable(VLVJEIndexCfg cfg,
                                               List<String> unacceptableReasons)
    {
      // TODO: validate more before returning true?
      return true;
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationDelete(VLVJEIndexCfg cfg)
    {
      ConfigChangeResult ccr;
      boolean adminActionRequired = false;
      ArrayList<String> messages = new ArrayList<String>();
      exclusiveLock.lock();
      try
      {
        VLVIndex vlvIndex =
            vlvIndexMap.get(cfg.getVLVIndexName().toLowerCase());
        vlvIndex.close();
        deleteDatabase(vlvIndex);
        vlvIndexMap.remove(cfg.getVLVIndexName());
      }
      catch(DatabaseException de)
      {
        messages.add(StaticUtils.stackTraceToSingleLineString(de));
        ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                     adminActionRequired,
                                     messages);
        return ccr;
      }
      finally
      {
        exclusiveLock.unlock();
      }
      return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                    messages);
    }
  }
  /**
   * A read write lock to handle schema changes and bulk changes.
   */
  private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
@@ -243,9 +447,20 @@
    // Instantiate the attribute indexes.
    attrIndexMap = new HashMap<AttributeType, AttributeIndex>();
    // Instantiate the VLV indexes.
    vlvIndexMap = new HashMap<String, VLVIndex>();
    config.addJEChangeListener(this);
    config.addJEIndexAddListener(this);
    config.addJEIndexDeleteListener(this);
    attributeJEIndexCfgManager =
        new AttributeJEIndexCfgManager();
    config.addJEIndexAddListener(attributeJEIndexCfgManager);
    config.addJEIndexDeleteListener(attributeJEIndexCfgManager);
    vlvJEIndexCfgManager =
        new VLVJEIndexCfgManager();
    config.addVLVJEIndexAddListener(vlvJEIndexCfgManager);
    config.addVLVJEIndexDeleteListener(vlvJEIndexCfgManager);
  }
  /**
@@ -298,6 +513,15 @@
        index.open();
        attrIndexMap.put(indexCfg.getIndexAttribute(), index);
      }
      for(String idx : config.listVLVJEIndexes())
      {
        VLVJEIndexCfg vlvIndexCfg = config.getVLVJEIndex(idx);
        VLVIndex vlvIndex = new VLVIndex(vlvIndexCfg, state, env, this);
        vlvIndex.open();
        vlvIndexMap.put(vlvIndexCfg.getVLVIndexName().toLowerCase(), vlvIndex);
      }
    }
    catch (DatabaseException de)
    {
@@ -326,8 +550,10 @@
    }
    config.removeJEChangeListener(this);
    config.removeJEIndexAddListener(this);
    config.removeJEIndexDeleteListener(this);
    config.removeJEIndexAddListener(attributeJEIndexCfgManager);
    config.removeJEIndexDeleteListener(attributeJEIndexCfgManager);
    config.removeVLVJEIndexDeleteListener(vlvJEIndexCfgManager);
    config.removeVLVJEIndexDeleteListener(vlvJEIndexCfgManager);
  }
  /**
@@ -397,6 +623,17 @@
  }
  /**
   * Look for an VLV index for the given index name.
   *
   * @param vlvIndexName The vlv index name for which an vlv index is needed.
   * @return The VLV index or null if there is none with that name.
   */
  public VLVIndex getVLVIndex(String vlvIndexName)
  {
    return vlvIndexMap.get(vlvIndexName);
  }
  /**
   * Retrieve all attribute indexes.
   *
   * @return All attribute indexes defined in this entry container.
@@ -406,6 +643,15 @@
    return attrIndexMap.values();
  }
  /**
   * Retrieve all VLV indexes.
   *
   * @return The collection of VLV indexes defined in this entry container.
   */
  public Collection<VLVIndex> getVLVIndexes()
  {
    return vlvIndexMap.values();
  }
  /**
   * Determine the highest entryID in the entryContainer.
@@ -448,9 +694,10 @@
   *          If a problem occurs while processing the
   *          search.
   * @throws DatabaseException If an error occurs in the JE database.
   * @throws JebException If an error occurs in the JE database.
   */
  public void search(SearchOperation searchOperation)
       throws DirectoryException, DatabaseException
       throws DirectoryException, DatabaseException, JebException
  {
    DN baseDN = searchOperation.getBaseDN();
    SearchScope searchScope = searchOperation.getScope();
@@ -617,54 +864,115 @@
      debugBuffer = new StringBuilder();
    }
    // Create an index filter to get the search result candidate entries.
    IndexFilter indexFilter =
         new IndexFilter(this, searchOperation, debugBuffer);
    // Evaluate the filter against the attribute indexes.
    EntryIDSet entryIDList = indexFilter.evaluate();
    // Evaluate the search scope against the id2children and id2subtree indexes.
    EntryIDSet entryIDList = null;
    boolean candidatesAreInScope = false;
    if (entryIDList.size() > IndexFilter.FILTER_CANDIDATE_THRESHOLD)
    if(sortRequest != null)
    {
      // Read the ID from dn2id.
      EntryID baseID = dn2id.get(null, baseDN);
      if (baseID == null)
      for(VLVIndex vlvIndex : vlvIndexMap.values())
      {
        int messageID = MSGID_JEB_SEARCH_NO_SUCH_OBJECT;
        String message = getMessage(messageID, baseDN.toString());
        DN matchedDN = getMatchedDN(baseDN);
        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
            message, messageID, matchedDN, null);
      }
      DatabaseEntry baseIDData = baseID.getDatabaseEntry();
      EntryIDSet scopeList;
      if (searchScope == SearchScope.SINGLE_LEVEL)
      {
        scopeList = id2children.readKey(baseIDData, null, LockMode.DEFAULT);
      }
      else
      {
        scopeList = id2subtree.readKey(baseIDData, null, LockMode.DEFAULT);
        if (searchScope == SearchScope.WHOLE_SUBTREE)
        try
        {
          // The id2subtree list does not include the base entry ID.
          scopeList.add(baseID);
          entryIDList =
              vlvIndex.evaluate(null, searchOperation, sortRequest, vlvRequest,
                                debugBuffer);
          if(entryIDList != null)
          {
            searchOperation.addResponseControl(
                new ServerSideSortResponseControl(LDAPResultCode.SUCCESS,
                                                  null));
            candidatesAreInScope = true;
            break;
          }
        }
        catch (DirectoryException de)
        {
          searchOperation.addResponseControl(
              new ServerSideSortResponseControl(
                  de.getResultCode().getIntValue(), null));
          if (sortRequest.isCritical())
          {
            throw de;
          }
        }
      }
      entryIDList.retainAll(scopeList);
      if (debugBuffer != null)
    }
    if(entryIDList == null)
    {
      // Create an index filter to get the search result candidate entries.
      IndexFilter indexFilter =
          new IndexFilter(this, searchOperation, debugBuffer);
      // Evaluate the filter against the attribute indexes.
      entryIDList = indexFilter.evaluate();
      // Evaluate the search scope against the id2children and id2subtree
      // indexes.
      if (entryIDList.size() > IndexFilter.FILTER_CANDIDATE_THRESHOLD)
      {
        debugBuffer.append(" scope=");
        debugBuffer.append(searchScope);
        scopeList.toString(debugBuffer);
        // Read the ID from dn2id.
        EntryID baseID = dn2id.get(null, baseDN);
        if (baseID == null)
        {
          int messageID = MSGID_JEB_SEARCH_NO_SUCH_OBJECT;
          String message = getMessage(messageID, baseDN.toString());
          DN matchedDN = getMatchedDN(baseDN);
          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
              message, messageID, matchedDN, null);
        }
        DatabaseEntry baseIDData = baseID.getDatabaseEntry();
        EntryIDSet scopeList;
        if (searchScope == SearchScope.SINGLE_LEVEL)
        {
          scopeList = id2children.readKey(baseIDData, null, LockMode.DEFAULT);
        }
        else
        {
          scopeList = id2subtree.readKey(baseIDData, null, LockMode.DEFAULT);
          if (searchScope == SearchScope.WHOLE_SUBTREE)
          {
            // The id2subtree list does not include the base entry ID.
            scopeList.add(baseID);
          }
        }
        entryIDList.retainAll(scopeList);
        if (debugBuffer != null)
        {
          debugBuffer.append(" scope=");
          debugBuffer.append(searchScope);
          scopeList.toString(debugBuffer);
        }
        if (scopeList.isDefined())
        {
          // In this case we know that every candidate is in scope.
          candidatesAreInScope = true;
        }
      }
      if (scopeList.isDefined())
      if (sortRequest != null)
      {
        // In this case we know that every candidate is in scope.
        candidatesAreInScope = true;
        try
        {
          entryIDList = EntryIDSetSorter.sort(this, entryIDList,
                                              searchOperation,
                                              sortRequest.getSortOrder(),
                                              vlvRequest);
          searchOperation.addResponseControl(
              new ServerSideSortResponseControl(LDAPResultCode.SUCCESS, null));
        }
        catch (DirectoryException de)
        {
          searchOperation.addResponseControl(
              new ServerSideSortResponseControl(
                  de.getResultCode().getIntValue(), null));
          if (sortRequest.isCritical())
          {
            throw de;
          }
        }
      }
    }
@@ -697,30 +1005,6 @@
    if (entryIDList.isDefined())
    {
      if (sortRequest != null)
      {
        try
        {
          entryIDList = EntryIDSetSorter.sort(this, entryIDList,
                                              searchOperation,
                                              sortRequest.getSortOrder(),
                                              vlvRequest);
          searchOperation.addResponseControl(
               new ServerSideSortResponseControl(LDAPResultCode.SUCCESS, null));
        }
        catch (DirectoryException de)
        {
          searchOperation.addResponseControl(
               new ServerSideSortResponseControl(
                        de.getResultCode().getIntValue(), null));
          if (sortRequest.isCritical())
          {
            throw de;
          }
        }
      }
      searchIndexed(entryIDList, candidatesAreInScope, searchOperation,
                    pageRequest);
    }
@@ -3313,6 +3597,11 @@
    {
      index.addEntry(txn, entryID, entry);
    }
    for (VLVIndex vlvIndex : vlvIndexMap.values())
    {
      vlvIndex.addEntry(txn, entryID, entry);
    }
  }
  /**
@@ -3332,6 +3621,11 @@
    {
      index.removeEntry(txn, entryID, entry);
    }
    for (VLVIndex vlvIndex : vlvIndexMap.values())
    {
      vlvIndex.removeEntry(txn, entryID, entry);
    }
  }
  /**
@@ -3344,11 +3638,13 @@
   * @param entryID The ID of the entry that was changed.
   * @param mods The sequence of modifications made to the entry.
   * @throws DatabaseException If an error occurs in the JE database.
   * @throws DirectoryException If a Directory Server error occurs.
   * @throws JebException If an error occurs in the JE backend.
   */
  private void indexModifications(Transaction txn, Entry oldEntry,
                                  Entry newEntry,
                                  EntryID entryID, List<Modification> mods)
      throws DatabaseException
      throws DatabaseException, DirectoryException, JebException
  {
    // Process in index configuration order.
    for (AttributeIndex index : attrIndexMap.values())
@@ -3370,6 +3666,11 @@
        index.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
      }
    }
    for(VLVIndex vlvIndex : vlvIndexMap.values())
    {
      vlvIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
    }
  }
  /**
@@ -3417,6 +3718,11 @@
    {
      index.listDatabases(dbList);
    }
    for (VLVIndex vlvIndex : vlvIndexMap.values())
    {
      dbList.add(vlvIndex);
    }
  }
  /**
@@ -3861,92 +4167,6 @@
  }
  /**
   * {@inheritDoc}
   */
  public synchronized boolean isConfigurationAddAcceptable(
      JEIndexCfg cfg, List<String> unacceptableReasons)
  {
    // TODO: validate more before returning true?
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized ConfigChangeResult applyConfigurationAdd(JEIndexCfg cfg)
  {
    ConfigChangeResult ccr;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    try
    {
      AttributeIndex index =
          new AttributeIndex(cfg, state, env, this);
      index.open();
      attrIndexMap.put(cfg.getIndexAttribute(), index);
    }
    catch(Exception e)
    {
      messages.add(StaticUtils.stackTraceToSingleLineString(e));
      ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                   adminActionRequired,
                                   messages);
      return ccr;
    }
    adminActionRequired = true;
    int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD;
    messages.add(getMessage(msgID, cfg.getIndexAttribute().getNameOrOID()));
    return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                  messages);
  }
  /**
   * {@inheritDoc}
   */
  public synchronized boolean isConfigurationDeleteAcceptable(
      JEIndexCfg cfg, List<String> unacceptableReasons)
  {
    // TODO: validate more before returning true?
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized ConfigChangeResult applyConfigurationDelete(
      JEIndexCfg cfg)
  {
    ConfigChangeResult ccr;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    exclusiveLock.lock();
    try
    {
      AttributeIndex index = attrIndexMap.get(cfg.getIndexAttribute());
      deleteAttributeIndex(index);
      attrIndexMap.remove(cfg.getIndexAttribute());
    }
    catch(DatabaseException de)
    {
      messages.add(StaticUtils.stackTraceToSingleLineString(de));
      ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
                                   adminActionRequired,
                                   messages);
      return ccr;
    }
    finally
    {
      exclusiveLock.unlock();
    }
    return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
                                  messages);
  }
  /**
   * Get the environment config of the JE environment used in this entry
   * container.
   *