Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Change log messages of noteDb case to match reviewDb case
  Add error handling if rename was not successful
  Upgrade bazlets to latest stable-2.16 to build with 2.16.13 API
  Add support for NoteDb

Change-Id: Icb56d3bddff6c217af1f53722f41548949f87144
diff --git a/README.md b/README.md
index 1ed0bf5..e14491a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 # Rename project plugin for Gerrit Code Review
 
-This plugin currently supports Gerrit version 2.14.X and 2.15.X with changes in reviewDb, noteDB is not yet supported.
+This plugin currently supports Gerrit version 2.14.X and 2.15.X with changes in reviewDb.
+Also supported is the noteDb alternative for Gerrit versions 2.15.X and above.
 
 For more information, see: `src/main/resources/Documentation/about.md`
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java
index 8d4f7ca..17ec0a7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java
@@ -152,7 +152,7 @@
   }
 
   void doRename(List<Change.Id> changeIds, ProjectResource rsrc, Input input, ProgressMonitor pm)
-      throws InterruptedException, ConfigInvalidException, IOException {
+      throws InterruptedException, ConfigInvalidException, IOException, RenameRevertException {
     Project.NameKey oldProjectKey = rsrc.getNameKey();
     Project.NameKey newProjectKey = new Project.NameKey(input.name);
     Exception ex = null;
@@ -161,8 +161,7 @@
       log.debug("Renamed the git repo to {} successfully.", newProjectKey.get());
       cacheHandler.update(rsrc.getProjectState().getProject(), newProjectKey);
 
-      List<Change.Id> updatedChangeIds =
-          dbHandler.rename(changeIds, newProjectKey, pm);
+      List<Change.Id> updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, pm);
       log.debug("Updated the changes in DB successfully for project {}.", oldProjectKey.get());
 
       // if the DB update is successful, update the secondary index
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java
new file mode 100644
index 0000000..d6252b9
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.googlesource.gerrit.plugins.renameproject;
+
+/** Add cause for exception during revert operation */
+public class RenameRevertException extends Exception {
+  public RenameRevertException(Throwable revertException, Throwable cause) {
+    super(
+        "Failed to revert after failed rename. Revert cause: " + cause.getMessage(),
+        revertException.initCause(cause));
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java
index 8c57dfc..97cd59f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/DatabaseRenameHandler.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.ServerInitiated;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.AccountsUpdate;
@@ -28,16 +27,14 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult;
-import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.renameproject.RenameRevertException;
 import com.googlesource.gerrit.plugins.renameproject.monitor.ProgressMonitor;
 import java.io.IOException;
-import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -53,28 +50,21 @@
   private static final Logger log = LoggerFactory.getLogger(DatabaseRenameHandler.class);
 
   private final ChangeNotes.Factory schemaFactory;
-  private final ChangeUpdate.Factory updateFactory;
   private final GitRepositoryManager repoManager;
-  private final Provider<CurrentUser> userProvider;
   private final Provider<InternalAccountQuery> accountQueryProvider;
   private final Provider<AccountsUpdate> accountsUpdateProvider;
-  private final Map<Change.Id, ChangeNotes> changeNotes = new HashMap<>();
 
   private Project.NameKey oldProjectKey;
 
   @Inject
   public DatabaseRenameHandler(
       ChangeNotes.Factory schemaFactory,
-      ChangeUpdate.Factory updateFactory,
       GitRepositoryManager repoManager,
-      Provider<CurrentUser> userProvider,
       Provider<InternalAccountQuery> accountQueryProvider,
       @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
     this.accountQueryProvider = accountQueryProvider;
     this.schemaFactory = schemaFactory;
-    this.updateFactory = updateFactory;
     this.repoManager = repoManager;
-    this.userProvider = userProvider;
     this.accountsUpdateProvider = accountsUpdateProvider;
   }
 
@@ -90,10 +80,9 @@
       ChangeNotesResult change = iterator.next();
       Change.Id changeId = change.id();
       changeIds.add(changeId);
-      changeNotes.put(changeId, change.notes());
     }
     log.debug(
-        "Number of changes related to the project {} are {}",
+        "Number of changes in noteDb related to project {} are {}",
         oldProjectKey.get(),
         changeIds.size());
     return changeIds;
@@ -101,62 +90,36 @@
 
   public List<Change.Id> rename(
       List<Change.Id> changes, Project.NameKey newProjectKey, ProgressMonitor pm)
-      throws IOException {
+      throws RenameRevertException, IOException, ConfigInvalidException {
     pm.beginTask("Updating changes in the database");
-    log.debug("Updating the changes in the DB related to project {}", oldProjectKey.get());
-    List<ChangeUpdate> updates = getChangeUpdates(changes, newProjectKey);
-    updateWatchEntries(newProjectKey);
-    List<Change> updated = new ArrayList<>();
+    log.debug("Updating the changes in noteDb related to project {}", oldProjectKey.get());
     try {
-      for (ChangeUpdate update : updates) {
-        update.commit();
-        updated.add(update.getChange());
-      }
-    } catch (IOException e) {
-      // TODO(mmiller): Consider covering this path with tests, albeit exceptional.
+      updateWatchEntries(newProjectKey);
+    } catch (Exception e) {
       log.error(
-          "Failed to update changes in the DB for the project {}, rolling back the operation.",
-          oldProjectKey.get());
-      rollback(updated, newProjectKey);
+          "Failed to update changes in noteDb for project {}, exception caught: {}. Rolling back the operation.",
+          oldProjectKey.get(),
+          e.toString());
+      try {
+        updateWatchEntries(newProjectKey);
+      } catch (Exception revertEx) {
+        log.error(
+            "Failed to rollback changes in noteDb from project {} to project {}, exception caught: {}",
+            newProjectKey.get(),
+            oldProjectKey.get(),
+            revertEx.toString());
+        throw new RenameRevertException(revertEx, e);
+      }
       throw e;
     }
+
     log.debug(
-        "Successfully updated the changes in the DB related to project {}", oldProjectKey.get());
+        "Successfully updated the changes in noteDb related to project {}", oldProjectKey.get());
     return changes;
   }
 
-  private List<ChangeUpdate> getChangeUpdates(List<Change.Id> changeIds, Project.NameKey nameKey) {
-    List<ChangeUpdate> updates = new ArrayList<>();
-    Date from = Date.from(Instant.now());
-    changeIds.forEach(
-        changeId -> {
-          ChangeNotes notes = schemaFactory.create(nameKey, changeId);
-          ChangeUpdate update = updateFactory.create(notes, userProvider.get(), from);
-          updates.add(update);
-        });
-    return updates;
-  }
-
-  private void rollback(List<Change> changes, Project.NameKey newProjectKey) {
-    Date from = Date.from(Instant.now());
-    changes.forEach(
-        change -> {
-          ChangeNotes notes = changeNotes.get(change.getId());
-          ChangeUpdate revert = updateFactory.create(notes, userProvider.get(), from);
-          revert.setRevertOf(change.getChangeId());
-          try {
-            revert.commit();
-          } catch (IOException ex) {
-            log.error(
-                "Failed to rollback change {} in DB from project {} to {}; rolling back others still.",
-                change.getChangeId(),
-                newProjectKey.get(),
-                oldProjectKey.get());
-          }
-        });
-  }
-
-  private void updateWatchEntries(Project.NameKey newProjectKey) {
+  private void updateWatchEntries(Project.NameKey newProjectKey)
+      throws IOException, ConfigInvalidException {
     for (AccountState a : accountQueryProvider.get().byWatchedProject(oldProjectKey)) {
       Account.Id accountId = a.getAccount().getId();
       ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
@@ -190,12 +153,14 @@
                 a.getUserName(),
                 newProjectKey.get(),
                 e);
+            throw e;
           } catch (IOException e) {
             log.error(
                 "Updating watch entry for user {} in project {} failed.",
                 a.getUserName(),
                 newProjectKey.get(),
                 e);
+            throw e;
           }
         }
       }