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

Jean-Noel Rouvignac
30.55.2014 4e23ba2d62363baf5bd5a0d2b7f474e0a8d75d64
OPENDJ-1613 (CR-5064) NullPointerException while searching on ch=changelog

The problem happens when doing a search in the change number changelog.
It only appears when there are two replicas under the same domain.
When creating the domain cursor, it is initialized from the content of the change number index DB. A replica cursor is created for each replica. The first replica cursor is initialized from the CSN coming from the change number index DB while the second replica cursor is initialized from the null CSN.
Under this circumstance, the second replica cursor is immediately added to the exhausted cursors of the domain cursor, and never gets out of it despite its replica DB being not empty.
The reason is because next() is never called on the the underlying ReplServerDBCursor.


JEReplicaDBCursor.java:
In next(), test whether the cursor has already been initialized (rather than verifying if a previous record exists) in order to verify if this is the first time next() is called.
Removed currentChange field which duplicates the content of ReplServerDBCursor.currentRecord .
Better handled the cursor closed state in all the methods.
1 files modified
38 ■■■■ changed files
opends/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDBCursor.java 38 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDBCursor.java
@@ -48,8 +48,13 @@
  private JEReplicaDB replicaDB;
  private final CSN startCSN;
  private CSN lastNonNullCurrentCSN;
  /**
   * The underlying replica DB cursor.
   * <p>
   * Initially <code>null</code>, the first call to {@link #next()} will
   * populate it. A call to {@link #close()} will set it to null again.
   */
  private ReplServerDBCursor cursor;
  private UpdateMsg currentChange;
  /**
   * Creates a new {@link JEReplicaDBCursor}. All created cursor must be
@@ -84,14 +89,24 @@
  @Override
  public UpdateMsg getRecord()
  {
    return currentChange;
    if (!isClosed() && cursor != null)
    {
      return cursor.getRecord();
    }
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public boolean next() throws ChangelogException
  {
    if (currentChange == null)
    if (isClosed())
    {
      return false;
    }
    final ReplServerDBCursor previousCursor = cursor;
    if (getRecord() == null)
    {
      synchronized (this)
      {
@@ -112,16 +127,16 @@
    }
    // For ON_MATCHING_KEY, do not call next() if the cursor has just been initialized.
    if (positionStrategy == ON_MATCHING_KEY && currentChange != null
    if (positionStrategy == ON_MATCHING_KEY && previousCursor != null
        || positionStrategy == AFTER_MATCHING_KEY)
    {
      cursor.next();
    }
    currentChange = cursor.getRecord();
    if (currentChange != null)
    final UpdateMsg currentRecord = cursor.getRecord();
    if (currentRecord != null)
    {
      lastNonNullCurrentCSN = currentChange.getCSN();
      lastNonNullCurrentCSN = currentRecord.getCSN();
      return true;
    }
    return false;
@@ -138,13 +153,17 @@
    }
  }
  private boolean isClosed()
  {
    return replicaDB == null;
  }
  private void closeCursor()
  {
    if (cursor != null)
    {
      cursor.close();
      cursor = null;
      currentChange = null;
    }
  }
@@ -164,8 +183,9 @@
  public String toString()
  {
    return getClass().getSimpleName()
        + " currentChange=" + cursor.getRecord()
        + " positionStrategy=" + positionStrategy
        + " currentChange=" + currentChange
        + " matchingStrategy=" + matchingStrategy
        + " replicaDB=" + replicaDB;
  }
}