From 36655d2a386c1f08ebe1a7c6d2e90b13e3673180 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Thu, 02 Jan 2014 11:13:58 +0000
Subject: [PATCH] OPENDJ-1262 NPE in ChangeNumberIndex during server startup 

---
 opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java                             |   24 ++++++++++++++----------
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java |   29 ++++++++++++++++++++++++++---
 2 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
index 20d6f64..33eaa30 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2013 ForgeRock AS
+ *      Copyright 2013-2014 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.je;
 
@@ -315,7 +315,8 @@
       for (Integer serverId : entry.getValue())
       {
         final CSN csn = mediumConsistencyRUV.getCSN(baseDN, serverId);
-        ensureCursorExists(baseDN, serverId, csn, false);
+        // start after the actual CSN when initializing from the previous cookie
+        ensureCursorExists(baseDN, serverId, csn);
       }
 
       ServerState latestKnownState = domainDB.getDomainNewestCSNs(baseDN);
@@ -362,8 +363,8 @@
     nextChangeForInsertDBCursor = result;
   }
 
-  private boolean ensureCursorExists(DN baseDN, Integer serverId, CSN csn,
-      boolean startFromPrecedingCSN) throws ChangelogException
+  private boolean ensureCursorExists(DN baseDN, Integer serverId,
+      CSN startAfterCSN) throws ChangelogException
   {
     Map<Integer, DBCursor<UpdateMsg>> map = allCursors.get(baseDN);
     if (map == null)
@@ -375,10 +376,6 @@
     if (cursor == null)
     {
       final ReplicationDomainDB domainDB = changelogDB.getReplicationDomainDB();
-      // start from preceding CSN for publishUpdateMsg(),
-      // or from the actual CSN when initializing from the previous cookie
-      final CSN startAfterCSN =
-          startFromPrecedingCSN ? getPrecedingCSN(csn) : csn;
       cursor = domainDB.getCursorFrom(baseDN, serverId, startAfterCSN);
       map.put(serverId, cursor);
       return false;
@@ -388,8 +385,12 @@
 
   /**
    * Returns the immediately preceding CSN.
+   *
+   * @param csn
+   *          the CSN to use
+   * @return the immediately preceding CSN or null if the provided CSN is null.
    */
-  private CSN getPrecedingCSN(CSN csn)
+  CSN getPrecedingCSN(CSN csn)
   {
     if (csn == null)
     {
@@ -602,7 +603,10 @@
         final Entry<Pair<DN, Integer>, CSN> entry = iter.next();
         final DN baseDN = entry.getKey().getFirst();
         final CSN csn = entry.getValue();
-        if (!ensureCursorExists(baseDN, csn.getServerId(), csn, true))
+        // start after preceding CSN so the first CSN read will exactly be the
+        // current one
+        final CSN startFromCSN = getPrecedingCSN(csn);
+        if (!ensureCursorExists(baseDN, csn.getServerId(), startFromCSN))
         {
           newCursorAdded = true;
         }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java
index 8af100b..83c1180 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexerTest.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2013 ForgeRock AS
+ *      Copyright 2013-2014 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.je;
 
@@ -304,7 +304,6 @@
     assertExternalChangelogContent(msg1, msg2, msg4);
   }
 
-
   private void addReplica(DN baseDN, int serverId) throws Exception
   {
     final SequentialDBCursor cursor = new SequentialDBCursor();
@@ -331,7 +330,10 @@
 
   private void stopCNIndexer()
   {
-    cnIndexer.initiateShutdown();
+    if (cnIndexer != null)
+    {
+      cnIndexer.initiateShutdown();
+    }
   }
 
   private ReplicatedUpdateMsg msg(DN baseDN, int serverId, long time)
@@ -458,4 +460,25 @@
       previousCookie.update(msg.getBaseDN(), msg.getCSN());
     }
   }
+
+  @DataProvider
+  public Object[][] precedingCSNData()
+  {
+    final int serverId = 42;
+    final int t = 1000;
+    return new Object[][] {
+      // @formatter:off
+      { null, null, },
+      { new CSN(t, 1, serverId), new CSN(t, 0, serverId), },
+      { new CSN(t, 0, serverId), new CSN(t - 1, Integer.MAX_VALUE, serverId), },
+      // @formatter:on
+    };
+  }
+
+  @Test(dataProvider = "precedingCSNData")
+  public void getPrecedingCSN(CSN start, CSN expected)
+  {
+    CSN precedingCSN = this.cnIndexer.getPrecedingCSN(start);
+    assertThat(precedingCSN).isEqualTo(expected);
+  }
 }

--
Gitblit v1.10.0