Use separate FlusherRunner to fix NullPointerExceptions

Autoreindex feature tracks all index changes and writes corresponding
time stamps in 4 separate files. This fix implements 1 flush runner per
each index type. This change decreases the amount of file operations and
fixes NullPointerExceptions.

Original patch used a single FlashRunner instance and always updated 4
index types. During a startup there was a huge possibility that members
changeTs, accountTs, groupTs, projectTs where not initialized at the
same time. This led to NullPointerException. The fix spreads flow and
updates only 1 index per Listener invocation.

Bug: Issue 14030
Change-Id: I066f8ac009f5e410ccef3066e379c02c2790149c
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
index 0e0602a..8abdb59 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
@@ -50,7 +50,10 @@
 
   private final Path dataDir;
   private final ScheduledExecutorService exec;
-  private final FlusherRunner flusher;
+  private final FlusherRunner changeFlusher;
+  private final FlusherRunner accountFlusher;
+  private final FlusherRunner groupFlusher;
+  private final FlusherRunner projectFlusher;
   private final SchemaFactory<ReviewDb> schemaFactory;
   private final ChangeFinder changeFinder;
   private final CurrentRequestContext currCtx;
@@ -61,16 +64,11 @@
   private volatile LocalDateTime projectTs;
 
   class FlusherRunner implements Runnable {
+    private final AbstractIndexRestApiServlet.IndexName index;
 
     @Override
     public void run() {
-      store(AbstractIndexRestApiServlet.IndexName.CHANGE, changeTs);
-      store(AbstractIndexRestApiServlet.IndexName.ACCOUNT, accountTs);
-      store(AbstractIndexRestApiServlet.IndexName.GROUP, groupTs);
-      store(AbstractIndexRestApiServlet.IndexName.PROJECT, projectTs);
-    }
-
-    private void store(AbstractIndexRestApiServlet.IndexName index, LocalDateTime latestTs) {
+      LocalDateTime latestTs = getIndexTimeStamp();
       Optional<LocalDateTime> currTs = getUpdateTs(index);
       if (!currTs.isPresent() || latestTs.isAfter(currTs.get())) {
         Path indexTsFile = dataDir.resolve(index.name().toLowerCase());
@@ -81,6 +79,25 @@
         }
       }
     }
+
+    FlusherRunner(AbstractIndexRestApiServlet.IndexName index) {
+      this.index = index;
+    }
+
+    private LocalDateTime getIndexTimeStamp() {
+      switch (index) {
+        case CHANGE:
+          return changeTs;
+        case GROUP:
+          return groupTs;
+        case ACCOUNT:
+          return accountTs;
+        case PROJECT:
+          return projectTs;
+        default:
+          throw new IllegalArgumentException("Unsupported index " + index);
+      }
+    }
   }
 
   @Inject
@@ -92,7 +109,10 @@
       CurrentRequestContext currCtx) {
     this.dataDir = dataDir;
     this.exec = queue.getDefaultQueue();
-    this.flusher = new FlusherRunner();
+    this.changeFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.CHANGE);
+    this.accountFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.ACCOUNT);
+    this.groupFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.GROUP);
+    this.projectFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.PROJECT);
     this.schemaFactory = schemaFactory;
     this.changeFinder = changeFinder;
     this.currCtx = currCtx;
@@ -152,19 +172,22 @@
     switch (index) {
       case CHANGE:
         changeTs = dateTime;
+        exec.execute(changeFlusher);
         break;
       case ACCOUNT:
         accountTs = dateTime;
+        exec.execute(accountFlusher);
         break;
       case GROUP:
         groupTs = dateTime;
+        exec.execute(groupFlusher);
         break;
       case PROJECT:
         projectTs = dateTime;
+        exec.execute(projectFlusher);
         break;
       default:
         throw new IllegalArgumentException("Unsupported index " + index);
     }
-    exec.execute(flusher);
   }
 }