From 2da62bc07d8ec56f15adb2c4128bf02fabb3885c Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Tue, 01 Jul 2014 08:57:03 +0000
Subject: [PATCH] Checkpoint commit for OPENDJ-1471 File based changelog : improve cursor behavior CR-3911

---
 opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java                                    |    5 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/SequentialDBCursor.java       |    8 +
 opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java                              |    1 
 opends/src/server/org/opends/server/replication/server/changelog/file/LogFile.java                                        |   22 -----
 opends/src/server/org/opends/server/replication/server/changelog/file/Log.java                                            |   21 ----
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java  |    4 
 opends/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java                                  |    4 -
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ReplicaOfflineCursorTest.java |    6 
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/CompositeDBCursorTest.java    |  111 ++++++++++++++++++++++++++-
 opends/src/server/org/opends/server/replication/server/changelog/api/DBCursor.java                                        |   26 ++++--
 opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java                                |    5 
 opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java                             |    4 -
 12 files changed, 143 insertions(+), 74 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/server/changelog/api/DBCursor.java b/opends/src/server/org/opends/server/replication/server/changelog/api/DBCursor.java
index 05e93cd..6970a9e 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/api/DBCursor.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/api/DBCursor.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2013 ForgeRock AS
+ *      Copyright 2013-2014 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.api;
 
@@ -32,15 +32,23 @@
  * anymore, a cursor must be closed to release all the resources into the
  * database.
  * <p>
- * Here is a typical usage pattern:
- *
+ * The cursor provides a java.sql.ResultSet like API : it is positioned before
+ * the first requested record and needs to be moved forward by calling
+ * {@link DBCursor#next()}.
+ * <p>
+ * Usage:
  * <pre>
- * DBCursor&lt;T&gt; cursor = ...;         // obtain a new cursor,
- *                                   // already initialized
- * T record1 = cursor.getRecord();   // get the first record
- * while (cursor.next()) {           // advance to the next record
- *   T record = cursor.getRecord();  // get the next record
- *   ...                             // etc.
+ * {@code
+ *  DBCursor cursor = ...;
+ *  try {
+ *    while (cursor.next()) {
+ *      Record record = cursor.getRecord();
+ *      // ... can call cursor.getRecord() again: it will return the same result
+ *    }
+ *  }
+ *  finally {
+ *    close(cursor);
+ *  }
  * }
  * </pre>
  *
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java b/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java
index 0c8df53..4639e7e 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java
@@ -96,8 +96,6 @@
    * replication domain starting after the provided {@link ServerState} for each
    * replicaDBs.
    * <p>
-   * The cursor is already advanced to the records after the serverState.
-   * <p>
    * When the cursor is not used anymore, client code MUST call the
    * {@link DBCursor#close()} method to free the resources and locks used by the
    * cursor.
@@ -119,8 +117,6 @@
    * Generates a {@link DBCursor} for one replicaDB for the specified
    * replication domain and serverId starting after the provided {@link CSN}.
    * <p>
-   * The cursor is already advanced to the records after the CSN.
-   * <p>
    * When the cursor is not used anymore, client code MUST call the
    * {@link DBCursor#close()} method to free the resources and locks used by the
    * cursor.
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java b/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
index 7c3163b..19e68b6 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
@@ -639,6 +639,7 @@
       // get the last already sent CSN from that server to get a cursor
       final CSN lastCSN = startAfterServerState != null ? startAfterServerState.getCSN(serverId) : null;
       final DBCursor<UpdateMsg> replicaDBCursor = getCursorFrom(baseDN, serverId, lastCSN);
+      replicaDBCursor.next();
       final CSN offlineCSN = getOfflineCSN(state, baseDN, serverId, startAfterServerState);
       cursors.put(new ReplicaOfflineCursor(replicaDBCursor, offlineCSN), null);
     }
@@ -673,9 +674,7 @@
     final FileReplicaDB replicaDB = getReplicaDB(baseDN, serverId);
     if (replicaDB != null)
     {
-      DBCursor<UpdateMsg> cursor = replicaDB.generateCursorFrom(startAfterCSN);
-      cursor.next();
-      return cursor;
+      return replicaDB.generateCursorFrom(startAfterCSN);
     }
     return EMPTY_CURSOR_REPLICA_DB;
   }
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java b/opends/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
index a4acf52..4e6cba9 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
@@ -206,10 +206,6 @@
    * Returns a cursor that allows to retrieve the update messages from this DB,
    * starting at the position defined by the smallest CSN that is strictly
    * higher than the provided CSN.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals {@code null} before any call to
-   * {@code cursor.next()} method.
    *
    * @param startAfterCSN
    *          The position where the cursor must start. If null, start from the
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/file/Log.java b/opends/src/server/org/opends/server/replication/server/changelog/file/Log.java
index c4dc1ca..16225d9 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/file/Log.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/file/Log.java
@@ -423,10 +423,6 @@
   /**
    * Returns a cursor that allows to retrieve the records from this log,
    * starting at the first position.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals to {@code null} before any call to
-   * {@code cursor.next()} method.
    *
    * @return a cursor on the log records, which is never {@code null}
    * @throws ChangelogException
@@ -461,10 +457,6 @@
   /**
    * Returns a cursor that allows to retrieve the records from this log,
    * starting at the position defined by the provided key.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals to {@code null} before any call to
-   * {@code cursor.next()} method.
    *
    * @param key
    *          Key to use as a start position for the cursor. If key is
@@ -482,11 +474,6 @@
    * Returns a cursor that allows to retrieve the records from this log,
    * starting at the position defined by the smallest key that is higher than
    * the provided key.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals to {@code null} before any call to
-   * {@code cursor.next()} method. After the first call to {@code cursor.next()}
-   * the cursor points to the record corresponding to the key found.
    *
    * @param key
    *          Key to use as a start position for the cursor. If key is
@@ -942,7 +929,9 @@
   }
 
   /**
-   * Represents a cursor than can be repositioned on a given key.
+   * Represents a DB Cursor than can be repositioned on a given key.
+   * <p>
+   * Note that as a DBCursor, it provides a java.sql.ResultSet like API.
    */
   static interface RepositionableCursor<K extends Comparable<K>, V> extends DBCursor<Record<K, V>>
   {
@@ -968,10 +957,6 @@
   /**
    * Implements a cursor on the log.
    * <p>
-   * The cursor initially points to a record, that is {@code cursor.getRecord()}
-   * is equals to the first record available from the cursor before any call to
-   * {@code cursor.next()} method.
-   * <p>
    * The cursor uses the log shared lock to ensure reads are not done during a rotation.
    * <p>
    * The cursor can be switched into an empty cursor by calling the {@code actAsEmptyCursor()}
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/file/LogFile.java b/opends/src/server/org/opends/server/replication/server/changelog/file/LogFile.java
index c27d38d..ff041ea 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/file/LogFile.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/file/LogFile.java
@@ -312,10 +312,6 @@
   /**
    * Returns a cursor that allows to retrieve the records from this log,
    * starting at the first position.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals {@code null} before any call to
-   * {@code cursor.next()} method.
    *
    * @return a cursor on the log records, which is never {@code null}
    * @throws ChangelogException
@@ -329,11 +325,6 @@
   /**
    * Returns a cursor that allows to retrieve the records from this log,
    * starting at the position defined by the provided key.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals to {@code null} before any call to
-   * {@code cursor.next()} method. After the first call to {@code cursor.next()}
-   * the cursor points to the record corresponding to the key.
    *
    * @param key
    *          Key to use as a start position for the cursor. If key is
@@ -351,11 +342,6 @@
    * Returns a cursor that allows to retrieve the records from this log,
    * starting at the position defined by the smallest key that is higher than
    * the provided key.
-   * <p>
-   * The returned cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals to {@code null} before any call to
-   * {@code cursor.next()} method. After the first call to {@code cursor.next()}
-   * the cursor points to the record corresponding to the key found.
    *
    * @param key
    *          Key to use as a start position for the cursor. If key is
@@ -571,13 +557,7 @@
     return logfile.equals(other.logfile);
   }
 
-  /**
-   * Implements a repositionable cursor on the log file.
-   * <p>
-   * The cursor initially points to no record, that is
-   * {@code cursor.getRecord()} is equals to {@code null} before any call to
-   * {@code cursor.next()} method.
-   */
+  /** Implements a repositionable cursor on the log file. */
   static final class LogFileCursor<K extends Comparable<K>, V> implements RepositionableCursor<K,V>
   {
     /** The underlying log on which entries are read. */
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java b/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
index 57cc0cf..052b0d0 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
@@ -483,6 +483,7 @@
     {
       final ReplicationDomainDB domainDB = changelogDB.getReplicationDomainDB();
       cursor = domainDB.getCursorFrom(baseDN, serverId, startAfterCSN);
+      cursor.next();
       map.put(serverId, cursor);
       return false;
     }
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java b/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java
index 52b3f2f..9d575dc 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java
@@ -714,6 +714,7 @@
       // get the last already sent CSN from that server to get a cursor
       final CSN lastCSN = startAfterServerState != null ? startAfterServerState.getCSN(serverId) : null;
       final DBCursor<UpdateMsg> replicaDBCursor = getCursorFrom(baseDN, serverId, lastCSN);
+      replicaDBCursor.next();
       final CSN offlineCSN = getOfflineCSN(state, baseDN, serverId, startAfterServerState);
       cursors.put(new ReplicaOfflineCursor(replicaDBCursor, offlineCSN), null);
     }
@@ -748,9 +749,7 @@
     JEReplicaDB replicaDB = getReplicaDB(baseDN, serverId);
     if (replicaDB != null)
     {
-      DBCursor<UpdateMsg> cursor = replicaDB.generateCursorFrom(startAfterCSN);
-      cursor.next();
-      return cursor;
+      return replicaDB.generateCursorFrom(startAfterCSN);
     }
     return EMPTY_CURSOR;
   }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java
index b91bdc1..4f7c836 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java
@@ -630,10 +630,8 @@
         final CSN csn = newestMsg.getCSN();
         when(cnIndexDB.getNewestRecord()).thenReturn(
             new ChangeNumberIndexRecord(initialCookie.toString(), baseDN, csn));
-        final SequentialDBCursor cursor =
-            cursors.get(Pair.of(baseDN, csn.getServerId()));
+        final SequentialDBCursor cursor = cursors.get(Pair.of(baseDN, csn.getServerId()));
         cursor.add(newestMsg);
-        cursor.next(); // simulate the cursor had been initialized with this change
       }
       initialCookie.update(msg.getBaseDN(), msg.getCSN());
     }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/CompositeDBCursorTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/CompositeDBCursorTest.java
index a2a2644..d4c32b5 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/CompositeDBCursorTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/CompositeDBCursorTest.java
@@ -29,6 +29,7 @@
 import java.util.Map;
 
 import org.opends.server.DirectoryServerTestCase;
+import org.opends.server.replication.common.CSN;
 import org.opends.server.replication.protocol.UpdateMsg;
 import org.opends.server.replication.server.changelog.api.ChangelogException;
 import org.opends.server.replication.server.changelog.api.DBCursor;
@@ -49,6 +50,8 @@
   private UpdateMsg msg2;
   private UpdateMsg msg3;
   private UpdateMsg msg4;
+  private UpdateMsg msg5;
+  private UpdateMsg msg6;
   private String baseDN1 = "dc=forgerock,dc=com";
   private String baseDN2 = "dc=example,dc=com";
 
@@ -59,6 +62,8 @@
     msg2 = new FakeUpdateMsg(2);
     msg3 = new FakeUpdateMsg(3);
     msg4 = new FakeUpdateMsg(4);
+    msg5 = new FakeUpdateMsg(5);
+    msg6 = new FakeUpdateMsg(6);
   }
 
   @Test
@@ -88,6 +93,17 @@
   }
 
   @Test
+  public void threeElementsCursor() throws Exception
+  {
+    final CompositeDBCursor<String> compCursor =
+        newCompositeDBCursor(of(new SequentialDBCursor(msg1, msg2, msg3), baseDN1));
+    assertInOrder(compCursor,
+        of(msg1, baseDN1),
+        of(msg2, baseDN1),
+        of(msg3, baseDN1));
+  }
+
+  @Test
   public void twoEmptyCursors() throws Exception
   {
     final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
@@ -121,7 +137,32 @@
   }
 
   @Test
-  public void recycleTwoElementCursors() throws Exception
+  public void twoThreeElementCursors() throws Exception
+  {
+    final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
+        of(new SequentialDBCursor(msg2, msg3, msg6), baseDN1),
+        of(new SequentialDBCursor(msg1, msg4, msg5), baseDN2));
+    assertInOrder(compCursor,
+        of(msg1, baseDN2),
+        of(msg2, baseDN1),
+        of(msg3, baseDN1),
+        of(msg4, baseDN2),
+        of(msg5, baseDN2),
+        of(msg6, baseDN1));
+  }
+
+  @Test
+  public void recycleTwoElementsCursor() throws Exception
+  {
+    final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
+        of(new SequentialDBCursor(msg1, null, msg2), baseDN1));
+    assertNextRecord(compCursor, of(msg1, baseDN1));
+    assertFalse(compCursor.next());
+    assertNextRecord(compCursor, of(msg2, baseDN1));
+  }
+
+  @Test
+  public void recycleTwoElementsCursors() throws Exception
   {
     final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
         of(new SequentialDBCursor(msg2, null, msg4), baseDN1),
@@ -133,6 +174,50 @@
         of(msg4, baseDN1));
   }
 
+  // TODO : this test fails because msg2 is returned twice
+  @Test(enabled=false)
+  public void recycleTwoElementsCursorsLongerExhaustion() throws Exception
+  {
+    final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
+        of(new SequentialDBCursor(null, null, msg1), baseDN1),
+        of(new SequentialDBCursor(msg2, msg3, msg4), baseDN2));
+    assertInOrder(compCursor,
+        of(msg2, baseDN2),
+        of(msg1, baseDN1),
+        of(msg3, baseDN2),
+        of(msg4, baseDN2));
+  }
+
+  @Test
+  public void recycleThreeElementsCursors() throws Exception
+  {
+    final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
+        of(new SequentialDBCursor(msg2, msg3, null, msg6), baseDN1),
+        of(new SequentialDBCursor(null, msg1, null, msg4, msg5), baseDN2));
+    assertInOrder(compCursor,
+        of(msg1, baseDN2),
+        of(msg2, baseDN1),
+        of(msg3, baseDN1),
+        of(msg4, baseDN2),
+        of(msg5, baseDN2),
+        of(msg6, baseDN1));
+  }
+
+  @Test
+  public void recycleThreeElementsCursorsLongerExhaustion() throws Exception
+  {
+    final CompositeDBCursor<String> compCursor = newCompositeDBCursor(
+        of(new SequentialDBCursor(msg2, msg3, null, msg6), baseDN1),
+        of(new SequentialDBCursor(null, msg1, null, null, msg4, msg5), baseDN2));
+    assertInOrder(compCursor,
+        of(msg1, baseDN2),
+        of(msg2, baseDN1),
+        of(msg3, baseDN1),
+        of(msg4, baseDN2),
+        of(msg5, baseDN2),
+        of(msg6, baseDN1));
+  }
+
   private CompositeDBCursor<String> newCompositeDBCursor(
       Pair<? extends DBCursor<UpdateMsg>, String>... pairs) throws Exception
   {
@@ -140,6 +225,9 @@
         new HashMap<DBCursor<UpdateMsg>, String>();
     for (Pair<? extends DBCursor<UpdateMsg>, String> pair : pairs)
     {
+      // The cursors in the composite are expected to be pointing
+      // to first record available
+      pair.getFirst().next();
       cursorsMap.put(pair.getFirst(), pair.getSecond());
     }
     return new CompositeDBCursor<String>(cursorsMap, true);
@@ -148,13 +236,26 @@
   private void assertInOrder(final CompositeDBCursor<String> compCursor,
       Pair<UpdateMsg, String>... expecteds) throws ChangelogException
   {
-    for (final Pair<UpdateMsg, String> expected : expecteds)
+    for (int i = 0; i < expecteds.length ; i++)
     {
-      assertTrue(compCursor.next());
-      assertSame(compCursor.getRecord(), expected.getFirst());
-      assertSame(compCursor.getData(), expected.getSecond());
+      final Pair<UpdateMsg, String> expected = expecteds[i];
+      final String index = " at element i=" + i;
+      assertTrue(compCursor.next(), index);
+      assertSame(compCursor.getRecord(), expected.getFirst(), index);
+      assertSame(compCursor.getData(), expected.getSecond(), index);
     }
     assertFalse(compCursor.next());
+    assertNull(compCursor.getRecord());
+    assertNull(compCursor.getData());
     compCursor.close();
   }
+
+  private void assertNextRecord(final CompositeDBCursor<String> compCursor,
+      Pair<UpdateMsg, String> expected) throws ChangelogException
+  {
+    assertTrue(compCursor.next());
+    assertSame(compCursor.getRecord(), expected.getFirst());
+    assertSame(compCursor.getData(), expected.getSecond());
+  }
+
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ReplicaOfflineCursorTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ReplicaOfflineCursorTest.java
index 1737f27..538b6de 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ReplicaOfflineCursorTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ReplicaOfflineCursorTest.java
@@ -65,7 +65,7 @@
   public void cursorReturnsTrue() throws Exception
   {
     final UpdateMsg updateMsg = new FakeUpdateMsg(timestamp++);
-    delegateCursor = new SequentialDBCursor(null, updateMsg);
+    delegateCursor = new SequentialDBCursor(updateMsg);
 
     final ReplicaOfflineCursor cursor = new ReplicaOfflineCursor(delegateCursor, null);
     assertThat(cursor.getRecord()).isNull();
@@ -95,7 +95,7 @@
   public void cursorReturnsUpdateMsgThenReplicaOfflineMsg() throws Exception
   {
     final UpdateMsg updateMsg = new FakeUpdateMsg(timestamp++);
-    delegateCursor = new SequentialDBCursor(null, updateMsg);
+    delegateCursor = new SequentialDBCursor(updateMsg);
 
     final CSN offlineCSN = new CSN(timestamp++, 1, 1);
     final ReplicaOfflineCursor cursor = new ReplicaOfflineCursor(delegateCursor, offlineCSN);
@@ -116,7 +116,7 @@
     final CSN outdatedOfflineCSN = new CSN(timestamp++, 1, 1);
 
     final UpdateMsg updateMsg = new FakeUpdateMsg(timestamp++);
-    delegateCursor = new SequentialDBCursor(null, updateMsg);
+    delegateCursor = new SequentialDBCursor(updateMsg);
 
     final ReplicaOfflineCursor cursor = new ReplicaOfflineCursor(delegateCursor, outdatedOfflineCSN);
     assertThat(cursor.getRecord()).isNull();
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/SequentialDBCursor.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/SequentialDBCursor.java
index f9d5d78..e0da0d5 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/SequentialDBCursor.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/SequentialDBCursor.java
@@ -39,10 +39,16 @@
   private final List<UpdateMsg> msgs;
   private UpdateMsg current;
 
+  /**
+   * A cursor built from a list of update messages.
+   * <p>
+   * This cursor provides a java.sql.ResultSet-like API to be consistent with the
+   * {@code DBCursor} API : it is positioned before the first requested record
+   * and needs to be moved forward by calling {@link DBCursor#next()}.
+   */
   public SequentialDBCursor(UpdateMsg... msgs)
   {
     this.msgs = new ArrayList<UpdateMsg>(Arrays.asList(msgs));
-    next();
   }
 
   public void add(UpdateMsg msg)

--
Gitblit v1.10.0