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

boli
08.19.2006 5c3196a7dc35588f22aa086e4e5cf6a563ec0de0
This fix removes the static nextId property as well as the getters and setters for it out of the EntryID class. The nextId property is now a non static property in the RootContainer class. Each RootContainer is responsible for keeping track of the next ID and assigning new entry IDs to all entries in its EntryContainers. 

The constructor for IndexIteratorAllIds now requires a RootContainer parameter.

The getIterator method in EntryIDSet class is also modified so it will no longer return a IndexIteratorAllIds object if no values are added to the set.

This fix also fixes the issue where performing a replaceEntryTransaction or renameEntryTransaction throws a NPE if a ModifyOperation is not specified. The backends API states that backend implementation should allow nulls for internal operations.

Thanks Neil for the code review.

Fix for issue 802 and issue 803
8 files modified
249 ■■■■■ changed files
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java 43 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java 62 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryID.java 43 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryIDSet.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/ImportJob.java 13 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/IndexIteratorAllIds.java 7 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/RootContainer.java 67 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/JebMessages.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -344,35 +344,23 @@
    // Preload the database cache.
    rootContainer.preload();
    // Determine the next entry ID and the total number of entries.
    EntryID highestID = null;
    long entryCount = 0;
    for (EntryContainer ec : rootContainer.getEntryContainers())
    try
    {
      try
      {
        EntryID id = ec.getHighestEntryID();
        if (highestID == null || id.compareTo(highestID) > 0)
        {
          highestID = id;
        }
        entryCount += ec.getEntryCount();
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "initializeBackend", e);
        String message = getMessage(MSGID_JEB_HIGHEST_ID_FAIL);
        throw new InitializationException(MSGID_JEB_HIGHEST_ID_FAIL,
                                          message, e);
      }
      // Log an informational message about the number of entries.
      int msgID = MSGID_JEB_BACKEND_STARTED;
      String message = getMessage(msgID, rootContainer.getEntryCount());
      logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
               msgID);
    }
    EntryID.initialize(highestID);
    // Log an informational message about the number of entries.
    int msgID = MSGID_JEB_BACKEND_STARTED;
    String message = getMessage(msgID, entryCount);
    logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message,
             msgID);
    catch(DatabaseException databaseException)
    {
      assert debugException(CLASS_NAME, "initializeBackend",
                            databaseException);
      String message = getMessage(MSGID_JEB_GET_ENTRY_COUNT_FAILED,
                                  databaseException.getMessage());
      throw new InitializationException(MSGID_JEB_GET_ENTRY_COUNT_FAILED,
                                        message, databaseException);
    }
    // Register this backend as a configurable component.
    DirectoryServer.registerConfigurableComponent(this);
@@ -400,6 +388,7 @@
    assert debugEnter(CLASS_NAME, "finalizeBackend");
    // Deregister our configurable components.
    // TODO: configurableEnv is always null and will not be deregistered.
    if (configurableEnv != null)
    {
      DirectoryServer.deregisterConfigurableComponent(configurableEnv);
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -125,6 +125,11 @@
  private Backend backend;
  /**
   * The root container in which this entryContainer belongs.
   */
  private RootContainer rootContainer;
  /**
   * The baseDN this entry container is responsible for.
   */
  private DN baseDN;
@@ -194,15 +199,17 @@
   *                container. It is needed by the Directory Server entry cache
   *                methods.
   * @param config The configuration of the JE backend.
   * @param env The JE environment to create this entryContainer in
   * @param env The JE environment to create this entryContainer in.
   * @param rootContainer The root container this entry container is in.
   */
  public EntryContainer(DN baseDN, Backend backend, Config config,
                        Environment env)
                        Environment env, RootContainer rootContainer)
  {
    this.backend = backend;
    this.baseDN = baseDN;
    this.config = config;
    this.env = env;
    this.rootContainer = rootContainer;
    // Instantiate the list of database handles.
    databases = new ArrayList<Database>();
@@ -493,10 +500,9 @@
   * The entryContainer must already be open.
   *
   * @return The highest entry ID.
   * @throws JebException If an error occurs in the JE backend.
   * @throws DatabaseException If an error occurs in the JE database.
   */
  public EntryID getHighestEntryID() throws JebException, DatabaseException
  public EntryID getHighestEntryID() throws DatabaseException
  {
    EntryID entryID = new EntryID(0);
    Cursor cursor = id2entry.openCursor(null, null);
@@ -1455,7 +1461,7 @@
      // First time through, assign the next entryID.
      if (entryID == null)
      {
        entryID = EntryID.assignNext();
        entryID = rootContainer.getNextEntryID();
      }
      // Insert into dn2id.
@@ -2573,7 +2579,12 @@
                                                        DirectoryException,
                                                        JebException
    {
      DN requestedNewSuperiorDN = modifyDNOperation.getNewSuperior();
      DN requestedNewSuperiorDN = null;
      if(modifyDNOperation != null)
      {
        requestedNewSuperiorDN = modifyDNOperation.getNewSuperior();
      }
      // Check whether the renamed entry already exists.
      if (dn2id.get(txn, newApexEntry.getDN()) != null)
@@ -2634,7 +2645,7 @@
          // renumber every entry that moves. This is even more
          // expensive since every entry has to be deleted from
          // and added back into the attribute indexes.
          newApexID = EntryID.assignNext();
          newApexID = rootContainer.getNextEntryID();
        }
      }
@@ -2710,7 +2721,7 @@
            EntryID newID = oldID;
            if (!newApexID.equals(oldApexID))
            {
              newID = EntryID.assignNext();
              newID = rootContainer.getNextEntryID();
            }
            // Move this entry.
@@ -2786,7 +2797,7 @@
      dn2uri.replaceEntry(txn, oldEntry, newEntry);
      // Remove the old ID from id2entry.
      if (!newID.equals(oldID))
      if (!newID.equals(oldID) || modifyDNOperation == null)
      {
        id2entry.remove(txn, oldID);
@@ -2866,10 +2877,11 @@
     * @param newEntry The new contents of the target entry.
     * @throws DirectoryException If a Directory Server error occurs.
     * @throws DatabaseException If an error occurs in the JE database.
     * @throws JebException if an error occurs in the JE database.
     */
    private void renameApexEntry(Transaction txn, EntryID entryID,
                                 Entry oldEntry, Entry newEntry)
         throws DirectoryException, DatabaseException
         throws DirectoryException, DatabaseException, JebException
    {
      DN oldDN = oldEntry.getDN();
      DN newDN = newEntry.getDN();
@@ -2892,9 +2904,20 @@
      // Replace the entry in id2entry.
      id2entry.put(txn, entryID, newEntry);
      // Update indexes only for those attributes that changed.
      indexModifications(txn, oldEntry, newEntry, entryID,
                         modifyDNOperation.getModifications());
      if(modifyDNOperation == null)
      {
        // Remove the old ID from the indexes.
        indexRemoveEntry(txn, oldEntry, entryID);
        // Insert the new ID into the indexes.
        indexInsertEntry(txn, newEntry, entryID);
      }
      else
      {
        // Update indexes only for those attributes that changed.
        indexModifications(txn, oldEntry, newEntry, entryID,
                           modifyDNOperation.getModifications());
      }
      // Remove the entry from the entry cache.
      EntryCache entryCache = DirectoryServer.getEntryCache();
@@ -3326,14 +3349,17 @@
   */
  public static boolean isManageDsaITOperation(Operation operation)
  {
    List<Control> controls = operation.getRequestControls();
    if (controls != null)
    if(operation != null)
    {
      for (Control control : controls)
      List<Control> controls = operation.getRequestControls();
      if (controls != null)
      {
        if (control.getOID().equals(ServerConstants.OID_MANAGE_DSAIT_CONTROL))
        for (Control control : controls)
        {
          return true;
          if (control.getOID().equals(ServerConstants.OID_MANAGE_DSAIT_CONTROL))
          {
            return true;
          }
        }
      }
    }
opends/src/server/org/opends/server/backends/jeb/EntryID.java
@@ -28,8 +28,6 @@
import com.sleepycat.je.DatabaseEntry;
import java.util.concurrent.atomic.AtomicLong;
/**
 * An integer identifier assigned to each entry in the JE backend.
 * An entry ID is implemented by this class as a long.
@@ -39,11 +37,6 @@
public class EntryID implements Comparable<EntryID>
{
  /**
   * The cached value of the next identifier to be assigned.
   */
  private static AtomicLong nextid = new AtomicLong(1);
  /**
   * The identifier integer value.
   */
  private final Long id;
@@ -105,42 +98,6 @@
  }
  /**
   * Initialize the next ID counter from the previous highest value.
   * @param highestID The previous highest entry ID.
   */
  public static void initialize(EntryID highestID)
  {
    nextid = new AtomicLong(highestID.id + 1);
  }
  /**
   * Assign the next entry ID.
   * @return The assigned entry ID.
   */
  public static EntryID assignNext()
  {
    return new EntryID(nextid.getAndIncrement());
  }
  /**
   * Return the lowest entry ID assigned.
   * @return The lowest entry ID assigned.
   */
  public static Long getLowest()
  {
    return 1L;
  }
  /**
   * Return the highest entry ID assigned.
   * @return The highest entry ID assigned.
   */
  public static Long getHighest()
  {
    return (nextid.get() - 1);
  }
  /**
   * Compares this object with the specified object for order.  Returns a
   * negative integer, zero, or a positive integer as this object is less
   * than, equal to, or greater than the specified object.<p>
opends/src/server/org/opends/server/backends/jeb/EntryIDSet.java
@@ -598,7 +598,7 @@
  }
  /**
   * Create an iterator over the set, or over the entire database
   * Create an iterator over the set or an empty iterator
   * if the set is not defined.
   *
   * @return An EntryID iterator.
@@ -608,7 +608,7 @@
    if (values == null)
    {
      // The set is not defined.
      return new IndexIteratorAllIds();
      return new IDSetIterator(new long[0]);
    }
    else
    {
opends/src/server/org/opends/server/backends/jeb/ImportJob.java
@@ -215,20 +215,12 @@
    rootContainer.openEntryContainers(config.getBaseDNs());
    // Create the import contexts for each base DN.
    EntryID highestID = null;
    DN baseDN;
    for (EntryContainer entryContainer : rootContainer.getEntryContainers())
    {
      baseDN = entryContainer.getBaseDN();
      // Keep track of the highest entry ID.
      EntryID id = entryContainer.getHighestEntryID();
      if (highestID == null || id.compareTo(highestID) > 0)
      {
        highestID = id;
      }
      // Create an import context.
      ImportContext importContext = new ImportContext();
      importContext.setBufferSize(bufferSize);
@@ -248,9 +240,6 @@
      importMap.put(baseDN, importContext);
    }
    // Initialize the entry ID generator.
    EntryID.initialize(highestID);
    // Make a note of the time we started.
    long startTime = System.currentTimeMillis();
@@ -691,7 +680,7 @@
        }
        // Assign a new entry identifier and write the new DN.
        entryID = EntryID.assignNext();
        entryID = rootContainer.getNextEntryID();
        dn2id.insert(txn, entryDN, entryID);
        // Construct a list of IDs up the DIT.
opends/src/server/org/opends/server/backends/jeb/IndexIteratorAllIds.java
@@ -35,9 +35,12 @@
{
  /**
   * Create a new iterator over all the entry IDs in the backend.
   *
   * @param rootContainer The root container where IDs from this
   *                     iterator will cover.
   */
  public IndexIteratorAllIds()
  public IndexIteratorAllIds(RootContainer rootContainer)
  {
    super(EntryID.getLowest(), EntryID.getHighest());
    super(rootContainer.getLowestEntryID(), rootContainer.getHighestEntryID());
  }
}
opends/src/server/org/opends/server/backends/jeb/RootContainer.java
@@ -40,6 +40,7 @@
import com.sleepycat.je.CheckpointConfig;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.*;
import java.io.File;
import java.io.FilenameFilter;
@@ -108,6 +109,11 @@
  private ConcurrentHashMap<DN, EntryContainer> entryContainers;
  /**
   * The cached value of the next entry identifier to be assigned.
   */
  private AtomicLong nextid = new AtomicLong(1);
  /**
   * Creates a new RootContainer object. Each root container represents a JE
   * environment.
   *
@@ -238,7 +244,7 @@
   */
  public EntryContainer openEntryContainer(DN baseDN) throws DatabaseException
  {
    EntryContainer ec = new EntryContainer(baseDN, backend, config, env);
    EntryContainer ec = new EntryContainer(baseDN, backend, config, env, this);
    EntryContainer ec1=this.entryContainers.get(baseDN);
    //If an entry container for this baseDN is already open we don't allow
    //another to be opened.
@@ -271,10 +277,19 @@
   */
  public void openEntryContainers(DN[] baseDNs) throws DatabaseException
  {
    EntryID id = null;
    EntryID highestID = null;
    for(DN baseDN : baseDNs)
    {
      openEntryContainer(baseDN);
      EntryContainer ec = openEntryContainer(baseDN);
      id = ec.getHighestEntryID();
      if(highestID == null || id.compareTo(highestID) > 0)
      {
        highestID = id;
      }
    }
    nextid = new AtomicLong(highestID.longValue() + 1);
  }
  /**
@@ -625,4 +640,52 @@
  {
    return env.getConfig();
  }
  /**
   * Get the total number of entries in this root container.
   *
   * @return The number of entries in this root container
   * @throws DatabaseException If an error occurs while retriving the entry
   *                           count.
   */
  public long getEntryCount() throws DatabaseException
  {
    long entryCount = 0;
    for(EntryContainer ec : this.entryContainers.values())
    {
      entryCount += ec.getEntryCount();
    }
    return entryCount;
  }
  /**
   * Assign the next entry ID.
   *
   * @return The assigned entry ID.
   */
  public EntryID getNextEntryID()
  {
    return new EntryID(nextid.getAndIncrement());
  }
  /**
   * Return the lowest entry ID assigned.
   *
   * @return The lowest entry ID assigned.
   */
  public Long getLowestEntryID()
  {
    return 1L;
  }
  /**
   * Return the highest entry ID assigned.
   *
   * @return The highest entry ID assigned.
   */
  public Long getHighestEntryID()
  {
    return (nextid.get() - 1);
  }
}
opends/src/server/org/opends/server/messages/JebMessages.java
@@ -1249,6 +1249,13 @@
      CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_WARNING | 128;
  /**
   * The message ID of an error indicating the entry count of a container can
   * not be determined.
   */
  public static final int MSGID_JEB_GET_ENTRY_COUNT_FAILED =
      CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_WARNING | 129;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -1743,5 +1750,8 @@
    registerMessage(MSGID_JEB_SET_PERMISSIONS_FAILED,
                    "Unable to set file permissions for the backend database " +
                    "directory %s.");
    registerMessage(MSGID_JEB_GET_ENTRY_COUNT_FAILED,
                    "Unable to determine the total number of entries in the " +
                    "container: %s");
  }
}