Merge branch 'stable-3.0' into stable-3.1

* stable-3.0:
  Fix NPE when the replicated refs is not found locally

Change-Id: I18c813f283517f520ed26a59ef3c3ccd6dceb88c
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
index c8ddc87..a0972c6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
@@ -32,7 +32,7 @@
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
-import com.googlesource.gerrit.plugins.replication.RefReplicatedEvent;
+import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent;
 import java.io.IOException;
 import java.util.Optional;
 import java.util.Set;
@@ -46,7 +46,6 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
 
 @Singleton
 public class ProjectVersionRefUpdate implements EventListener {
@@ -76,27 +75,21 @@
       updateProducerProjectVersionUpdate((RefUpdatedEvent) event);
     }
 
-    // Consumers of the Event use RefReplicatedEvent to trigger the version update
-    if (Context.isForwardedEvent() && event instanceof RefReplicatedEvent) {
-      updateConsumerProjectVersion((RefReplicatedEvent) event);
+    // Consumers of the Event use RefReplicationDoneEvent to trigger the version update
+    if (Context.isForwardedEvent() && event instanceof RefReplicationDoneEvent) {
+      updateConsumerProjectVersion((RefReplicationDoneEvent) event);
     }
   }
 
-  private void updateConsumerProjectVersion(RefReplicatedEvent refReplicatedEvent) {
-    Project.NameKey projectNameKey = refReplicatedEvent.getProjectNameKey();
-    if (!refReplicatedEvent.refStatus.equals(RemoteRefUpdate.Status.OK)) {
-      logger.atFine().log(
-          String.format(
-              "Skipping version update for %s. RefReplicatedEvent failed with %s",
-              projectNameKey.get(), refReplicatedEvent.refStatus));
-      return;
-    }
-    if (refReplicatedEvent.getRefName().startsWith(SEQUENCE_REF_PREFIX)) {
+  private void updateConsumerProjectVersion(RefReplicationDoneEvent refReplicationDoneEvent) {
+    Project.NameKey projectNameKey = refReplicationDoneEvent.getProjectNameKey();
+
+    if (refReplicationDoneEvent.getRefName().startsWith(SEQUENCE_REF_PREFIX)) {
       logger.atFine().log("Found Sequence ref, skipping update for " + projectNameKey.get());
       return;
     }
     try {
-      updateLocalProjectVersion(projectNameKey, refReplicatedEvent.getRefName());
+      updateLocalProjectVersion(projectNameKey, refReplicationDoneEvent.getRefName());
     } catch (LocalProjectVersionUpdateException e) {
       logger.atSevere().withCause(e).log(
           "Issue encountered when updating version for project " + projectNameKey);
@@ -112,10 +105,17 @@
     try {
       Project.NameKey projectNameKey = refUpdatedEvent.getProjectNameKey();
       Ref currentProjectVersionRef = getLocalProjectVersionRef(refUpdatedEvent.getProjectNameKey());
-      ObjectId newProjectVersionObjectId =
+      Optional<ObjectId> newProjectVersionObjectId =
           updateLocalProjectVersion(projectNameKey, refUpdatedEvent.getRefName());
-      updateSharedProjectVersion(
-          projectNameKey, currentProjectVersionRef, newProjectVersionObjectId);
+
+      if (newProjectVersionObjectId.isPresent()) {
+        updateSharedProjectVersion(
+            projectNameKey, currentProjectVersionRef, newProjectVersionObjectId.get());
+      } else {
+        logger.atWarning().log(
+            "Ref %s not found on projet %s: skipping project version update",
+            refUpdatedEvent.getRefName(), projectNameKey);
+      }
     } catch (LocalProjectVersionUpdateException | SharedProjectVersionUpdateException e) {
       logger.atSevere().withCause(e).log(
           "Issue encountered when updating version for project "
@@ -151,16 +151,20 @@
     }
   }
 
-  private Long getLastRefUpdatedTimestamp(Project.NameKey projectNameKey, String refName)
+  private Optional<Long> getLastRefUpdatedTimestamp(Project.NameKey projectNameKey, String refName)
       throws LocalProjectVersionUpdateException {
     logger.atFine().log(
         String.format(
             "Getting last ref updated time for project %s, ref %s", projectNameKey.get(), refName));
     try (Repository repository = gitRepositoryManager.openRepository(projectNameKey)) {
       Ref ref = repository.findRef(refName);
+      if (ref == null) {
+        logger.atWarning().log("Unable to find ref " + refName + " in project " + projectNameKey);
+        return Optional.empty();
+      }
       try (RevWalk walk = new RevWalk(repository)) {
         RevCommit commit = walk.parseCommit(ref.getObjectId());
-        return Integer.toUnsignedLong(commit.getCommitTime());
+        return Optional.of(Integer.toUnsignedLong(commit.getCommitTime()));
       }
     } catch (IOException ioe) {
       String message =
@@ -252,22 +256,26 @@
     }
   }
 
-  private ObjectId updateLocalProjectVersion(Project.NameKey projectNameKey, String refName)
-      throws LocalProjectVersionUpdateException {
-    Long lastRefUpdatedTimestamp = getLastRefUpdatedTimestamp(projectNameKey, refName);
+  private Optional<ObjectId> updateLocalProjectVersion(
+      Project.NameKey projectNameKey, String refName) throws LocalProjectVersionUpdateException {
+    Optional<Long> lastRefUpdatedTimestamp = getLastRefUpdatedTimestamp(projectNameKey, refName);
+    if (!lastRefUpdatedTimestamp.isPresent()) {
+      return Optional.empty();
+    }
+
     logger.atFine().log("Updating local version for project " + projectNameKey.get());
     try (Repository repository = gitRepositoryManager.openRepository(projectNameKey)) {
-      RefUpdate refUpdate = getProjectVersionRefUpdate(repository, lastRefUpdatedTimestamp);
+      RefUpdate refUpdate = getProjectVersionRefUpdate(repository, lastRefUpdatedTimestamp.get());
       RefUpdate.Result result = refUpdate.update();
       if (!isSuccessful(result)) {
         String message =
             String.format(
                 "RefUpdate failed with result %s for: project=%s, version=%d",
-                result.name(), projectNameKey.get(), lastRefUpdatedTimestamp);
+                result.name(), projectNameKey.get(), lastRefUpdatedTimestamp.get());
         logger.atSevere().log(message);
         throw new LocalProjectVersionUpdateException(message);
       }
-      return refUpdate.getNewObjectId();
+      return Optional.of(refUpdate.getNewObjectId());
     } catch (IOException e) {
       String message = "Cannot create versioning command for " + projectNameKey.get();
       logger.atSevere().withCause(e).log(message);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
index 776fec8..63415e0 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
@@ -29,6 +29,7 @@
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.RefFixture;
 import com.googlesource.gerrit.plugins.replication.RefReplicatedEvent;
+import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent;
 import com.googlesource.gerrit.plugins.replication.ReplicationState;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -126,15 +127,10 @@
   }
 
   @Test
-  public void consumerShouldUpdateProjectVersionUponRefReplicatedEvent() throws IOException {
+  public void consumerShouldUpdateProjectVersionUponRefReplicationDoneEvent() throws IOException {
     Context.setForwardedEvent(true);
-    RefReplicatedEvent refReplicatedEvent =
-        new RefReplicatedEvent(
-            A_TEST_PROJECT_NAME,
-            A_TEST_REF_NAME,
-            "targetNode",
-            ReplicationState.RefPushResult.SUCCEEDED,
-            RemoteRefUpdate.Status.OK);
+    RefReplicationDoneEvent refReplicatedEvent =
+        new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, A_TEST_REF_NAME, 1);
 
     new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent);
 
@@ -205,6 +201,7 @@
   public void getLocalProjectVersionShouldReturnCorrectValue() throws IOException {
     updateLocalVersion();
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
+    assertThat(ref).isNotNull();
 
     Optional<Long> version =
         new ProjectVersionRefUpdate(repoManager, sharedRefDb)
@@ -216,13 +213,8 @@
 
   private void updateLocalVersion() {
     Context.setForwardedEvent(true);
-    RefReplicatedEvent refReplicatedEvent =
-        new RefReplicatedEvent(
-            A_TEST_PROJECT_NAME,
-            A_TEST_REF_NAME,
-            "targetNode",
-            ReplicationState.RefPushResult.SUCCEEDED,
-            RemoteRefUpdate.Status.OK);
+    RefReplicationDoneEvent refReplicatedEvent =
+        new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, A_TEST_REF_NAME, 1);
     new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent);
   }
 }