Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Upgrade bazlets to latest stable-2.16 to build with 2.16.26 API
  Upgrade bazlets to latest stable-2.16 to build with 2.16.23 API

Change-Id: I1aaee339d8cd7bd22a5fa8a349a5000b8b97f4fb
diff --git a/.bazelversion b/.bazelversion
index fd2a018..7c69a55 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.1.0
+3.7.0
diff --git a/WORKSPACE b/WORKSPACE
index 27db984..e658752 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,26 +3,15 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "6c39deb06f58bb62162ccb6865964f531739f512",
+    commit = "a010a539824bd467059f414412a47b80101352d7",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
-# Snapshot Plugin API
-#load(
-#    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
-#    "gerrit_api_maven_local",
-#)
-
-# Load snapshot Plugin API
-#gerrit_api_maven_local()
-
-# Release Plugin API
 load(
     "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
     "gerrit_api",
 )
 
-# Load release Plugin API
 gerrit_api()
 
 load("//:external_plugin_deps.bzl", "external_plugin_deps")
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java
index 04426bc..170e0d6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/LockUnlockProject.java
@@ -28,17 +28,22 @@
 public class LockUnlockProject {
   private final MetaDataUpdate.Server metaDataUpdateFactory;
   private final ProjectCache projectCache;
+  private final ProjectConfig.Factory projectConfigFactory;
 
   @Inject
-  LockUnlockProject(MetaDataUpdate.Server metaDataUpdateFactory, ProjectCache projectCache) {
+  LockUnlockProject(
+      MetaDataUpdate.Server metaDataUpdateFactory,
+      ProjectCache projectCache,
+      ProjectConfig.Factory projectConfigFactory) {
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.projectCache = projectCache;
+    this.projectConfigFactory = projectConfigFactory;
   }
 
   public void lock(Project.NameKey key) throws IOException, ConfigInvalidException {
     MetaDataUpdate md = metaDataUpdateFactory.create(key);
 
-    ProjectConfig projectConfig = ProjectConfig.read(md);
+    ProjectConfig projectConfig = projectConfigFactory.read(md);
     Project p = projectConfig.getProject();
     p.setState(ProjectState.READ_ONLY);
 
@@ -50,7 +55,7 @@
   public void unlock(Project.NameKey key) throws IOException, ConfigInvalidException {
     MetaDataUpdate md = metaDataUpdateFactory.create(key);
 
-    ProjectConfig projectConfig = ProjectConfig.read(md);
+    ProjectConfig projectConfig = projectConfigFactory.read(md);
     Project p = projectConfig.getProject();
     p.setState(ProjectState.ACTIVE);
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java
index 6962811..568a7be 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/Module.java
@@ -14,12 +14,14 @@
 
 package com.googlesource.gerrit.plugins.renameproject;
 
+import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
 import static com.googlesource.gerrit.plugins.renameproject.RenameOwnProjectCapability.RENAME_OWN_PROJECT;
 import static com.googlesource.gerrit.plugins.renameproject.RenameProjectCapability.RENAME_PROJECT;
 
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.config.CapabilityDefinition;
 import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.restapi.RestApiModule;
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.UniqueAnnotations;
 import com.googlesource.gerrit.plugins.renameproject.cache.CacheRenameHandler;
@@ -45,5 +47,13 @@
     bind(RenamePreconditions.class);
     bind(IndexUpdateHandler.class);
     bind(RevertRenameProject.class);
+
+    install(
+        new RestApiModule() {
+          @Override
+          protected void configure() {
+            post(PROJECT_KIND, "rename").to(RenameProject.class);
+          }
+        });
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java
index 14b5898..80b1306 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameCommand.java
@@ -14,17 +14,16 @@
 
 package com.googlesource.gerrit.plugins.renameproject;
 
+import static com.googlesource.gerrit.plugins.renameproject.RenameProject.CANCELLATION_MSG;
 import static com.googlesource.gerrit.plugins.renameproject.RenameProject.WARNING_LIMIT;
 
 import com.google.gerrit.extensions.restapi.RestApiException;
 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.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.googlesource.gerrit.plugins.renameproject.monitor.CommandProgressMonitor;
@@ -33,6 +32,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.List;
+import java.util.Optional;
 import org.kohsuke.args4j.Argument;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,23 +40,18 @@
 @CommandMetaData(name = "rename", description = "Rename project")
 public final class RenameCommand extends SshCommand {
   @Argument(index = 0, required = true, metaVar = "OLDPROJECT", usage = "project to rename")
-  private String projectControl;
+  private ProjectState projectState;
 
   @Argument(index = 1, required = true, metaVar = "NEWNAME", usage = "new name for the project")
   private String newProjectName;
 
   private static final Logger log = LoggerFactory.getLogger(RenameCommand.class);
   private final RenameProject renameProject;
-  private final Provider<ProjectCache> projectCacheProvider;
   private final Provider<CurrentUser> self;
 
   @Inject
-  protected RenameCommand(
-      RenameProject renameProject,
-      Provider<ProjectCache> projectCacheProvider,
-      Provider<CurrentUser> self) {
+  protected RenameCommand(RenameProject renameProject, Provider<CurrentUser> self) {
     this.renameProject = renameProject;
-    this.projectCacheProvider = projectCacheProvider;
     this.self = self;
   }
 
@@ -65,22 +60,19 @@
     try {
       RenameProject.Input input = new RenameProject.Input();
       input.name = newProjectName;
-      ProjectResource rsrc =
-          new ProjectResource(
-              projectCacheProvider.get().get(new Project.NameKey(projectControl)), self.get());
+      ProjectResource rsrc = new ProjectResource(projectState, self.get());
       try (CommandProgressMonitor monitor = new CommandProgressMonitor(stdout)) {
-        renameProject.assertCanRename(rsrc, input, monitor);
-        List<Change.Id> changeIds = renameProject.getChanges(rsrc, monitor);
+        renameProject.assertCanRename(rsrc, input, Optional.of(monitor));
+        List<Change.Id> changeIds = renameProject.getChanges(rsrc, Optional.of(monitor));
         if (continueRename(changeIds, monitor)) {
-          renameProject.doRename(changeIds, rsrc, input, monitor);
+          renameProject.doRename(changeIds, rsrc, input, Optional.of(monitor));
         } else {
-          String cancellationMsg = "Rename operation was cancelled by user.";
-          log.debug(cancellationMsg);
-          stdout.println(cancellationMsg);
+          log.debug(CANCELLATION_MSG);
+          stdout.println(CANCELLATION_MSG);
           stdout.flush();
         }
       }
-    } catch (RestApiException | OrmException | IOException e) {
+    } catch (RestApiException | IOException e) {
       throw die(e);
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java
index 88c0e3b..6449cba 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLog.java
@@ -17,10 +17,10 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.extensions.systemstatus.ServerInformation;
+import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.AuditEvent;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.OutputFormat;
 import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.util.PluginLogFile;
 import com.google.gerrit.server.util.SystemLog;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java
index 5a11a17..053e8b9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameLogLayout.java
@@ -33,8 +33,8 @@
    * +0100] INFO 1000000 admin OK \ myProject {"name": newName}
    *
    * <p>The log entry for a failed project rename will look like this: [2015-03-05 12:14:30,180
-   * +0100] ERROR 1000000 admin FAIL \ myProject {"name": newName}
-   * com.google.gwtorm.server.OrmException: \ Failed to access the database
+   * +0100] ERROR 1000000 admin FAIL \ myProject {"name": newName} TODO(mmiller): \ Failed to access
+   * the database
    */
   @Override
   public String format(LoggingEvent event) {
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 d1f1ba5..c7937b6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameProject.java
@@ -25,7 +25,10 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Change.Id;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
@@ -35,11 +38,11 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
+import com.googlesource.gerrit.plugins.renameproject.RenameProject.Input;
 import com.googlesource.gerrit.plugins.renameproject.cache.CacheRenameHandler;
 import com.googlesource.gerrit.plugins.renameproject.conditions.RenamePreconditions;
 import com.googlesource.gerrit.plugins.renameproject.database.DatabaseRenameHandler;
@@ -49,18 +52,39 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Singleton
-public class RenameProject {
+public class RenameProject implements RestModifyView<ProjectResource, Input> {
+
+  @Override
+  public Object apply(ProjectResource resource, Input input)
+      throws IOException, AuthException, BadRequestException, ResourceConflictException,
+          InterruptedException, ConfigInvalidException, RenameRevertException {
+    assertCanRename(resource, input, Optional.empty());
+    List<Id> changeIds = getChanges(resource, Optional.empty());
+
+    if (changeIds == null || changeIds.size() <= WARNING_LIMIT || input.continueWithRename) {
+      doRename(changeIds, resource, input, Optional.empty());
+    } else {
+      log.debug(CANCELLATION_MSG);
+      return Response.none();
+    }
+    return Response.ok("");
+  }
 
   static class Input {
     String name;
+    boolean continueWithRename;
   }
 
   static final int WARNING_LIMIT = 5000;
+  static final String CANCELLATION_MSG =
+      "Rename cancelled due to number of changes exceeding warning limit and user's will to not continue";
+
   private static final Logger log = LoggerFactory.getLogger(RenameProject.class);
   private static final String CACHE_NAME = "changeid_project";
 
@@ -147,10 +171,11 @@
     return true;
   }
 
-  void assertCanRename(ProjectResource rsrc, Input input, ProgressMonitor pm)
+  void assertCanRename(ProjectResource rsrc, Input input, Optional<ProgressMonitor> pm)
       throws ResourceConflictException, BadRequestException, AuthException {
     try {
-      pm.beginTask("Checking preconditions");
+      pm.ifPresent(progressMonitor -> progressMonitor.beginTask("Checking preconditions"));
+
       assertNewNameNotNull(input);
       assertRenamePermission(rsrc);
       renamePreconditions.assertCanRename(rsrc, new Project.NameKey(input.name));
@@ -160,8 +185,9 @@
     }
   }
 
-  void doRename(List<Change.Id> changeIds, ProjectResource rsrc, Input input, ProgressMonitor pm)
-      throws InterruptedException, OrmException, ConfigInvalidException, IOException {
+  void doRename(
+      List<Change.Id> changeIds, ProjectResource rsrc, Input input, Optional<ProgressMonitor> pm)
+      throws InterruptedException, ConfigInvalidException, IOException, RenameRevertException {
     Project.NameKey oldProjectKey = rsrc.getNameKey();
     Project.NameKey newProjectKey = new Project.NameKey(input.name);
     Exception ex = null;
@@ -212,7 +238,7 @@
   }
 
   void fsRenameStep(
-      Project.NameKey oldProjectKey, Project.NameKey newProjectKey, ProgressMonitor pm)
+      Project.NameKey oldProjectKey, Project.NameKey newProjectKey, Optional<ProgressMonitor> pm)
       throws IOException {
     fsHandler.rename(oldProjectKey, newProjectKey, pm);
     logPerformedStep(Step.FILESYSTEM, newProjectKey, oldProjectKey);
@@ -228,10 +254,9 @@
       List<Change.Id> changeIds,
       Project.NameKey oldProjectKey,
       Project.NameKey newProjectKey,
-      ProgressMonitor pm)
-      throws OrmException {
-    List<Change.Id> updatedChangeIds =
-        dbHandler.rename(changeIds, oldProjectKey, newProjectKey, pm);
+      Optional<ProgressMonitor> pm)
+      throws IOException, ConfigInvalidException, RenameRevertException {
+    List<Change.Id> updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, pm);
     logPerformedStep(Step.DATABASE, newProjectKey, oldProjectKey);
     return updatedChangeIds;
   }
@@ -240,7 +265,7 @@
       List<Change.Id> updatedChangeIds,
       Project.NameKey oldProjectKey,
       Project.NameKey newProjectKey,
-      ProgressMonitor pm)
+      Optional<ProgressMonitor> pm)
       throws InterruptedException {
     indexHandler.updateIndex(updatedChangeIds, newProjectKey, pm);
     logPerformedStep(Step.INDEX, newProjectKey, oldProjectKey);
@@ -276,9 +301,9 @@
     return stepsPerformed;
   }
 
-  List<Change.Id> getChanges(ProjectResource rsrc, ProgressMonitor pm)
-      throws OrmException, IOException {
-    pm.beginTask("Retrieving the list of changes from DB");
+  List<Change.Id> getChanges(ProjectResource rsrc, Optional<ProgressMonitor> opm)
+      throws IOException {
+    opm.ifPresent(pm -> pm.beginTask("Retrieving the list of changes from DB"));
     Project.NameKey oldProjectKey = rsrc.getNameKey();
     return dbHandler.getChangeIds(oldProjectKey);
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java
index 0cb6256..b0e467a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RenameRevertException.java
@@ -13,10 +13,8 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.renameproject;
 
-import com.google.gwtorm.server.OrmException;
-
 /** Add cause for exception during revert operation */
-public class RenameRevertException extends OrmException {
+public class RenameRevertException extends Exception {
   private static final long serialVersionUID = 1L;
 
   public RenameRevertException(Throwable revertException, Throwable cause) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java
index 5de66f2..0671c71 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProject.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Change.Id;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.renameproject.RenameProject.Step;
 import com.googlesource.gerrit.plugins.renameproject.cache.CacheRenameHandler;
@@ -28,6 +27,8 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,13 +57,13 @@
       List<Id> changeIds,
       Project.NameKey oldProjectKey,
       Project.NameKey newProjectKey,
-      ProgressMonitor pm)
-      throws IOException, OrmException {
-    pm.beginTask("Reverting the rename procedure.");
+      Optional<ProgressMonitor> opm)
+      throws IOException, RenameRevertException, ConfigInvalidException {
+    opm.ifPresent(pm -> pm.beginTask("Reverting the rename procedure."));
     List<Change.Id> updatedChangeIds = Collections.emptyList();
     if (stepsPerformed.contains(Step.FILESYSTEM)) {
       try {
-        fsHandler.rename(newProjectKey, oldProjectKey, pm);
+        fsHandler.rename(newProjectKey, oldProjectKey, opm);
         log.debug("Reverted the git repo name to {} successfully.", oldProjectKey.get());
       } catch (IOException e) {
         log.error(
@@ -76,12 +77,12 @@
     }
     if (stepsPerformed.contains(Step.DATABASE)) {
       try {
-        updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, oldProjectKey, pm);
+        updatedChangeIds = dbHandler.rename(changeIds, newProjectKey, opm);
         log.debug(
             "Reverted the changes in DB successfully from project {} to project {}.",
             newProjectKey.get(),
             oldProjectKey.get());
-      } catch (OrmException e) {
+      } catch (RenameRevertException | ConfigInvalidException e) {
         log.error(
             "Failed to revert changes in DB for project {}. Secondary indexes not reverted. Exception caught: {}",
             oldProjectKey.get(),
@@ -91,7 +92,7 @@
     }
     if (stepsPerformed.contains(Step.INDEX)) {
       try {
-        indexHandler.updateIndex(updatedChangeIds, oldProjectKey, pm);
+        indexHandler.updateIndex(updatedChangeIds, oldProjectKey, opm);
         log.debug(
             "Reverted the secondary index successfully from project {} to project {}.",
             newProjectKey.get(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java
index 9efa51d..3e30f6e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/conditions/RenamePreconditions.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.submit.MergeOpRepoManager;
 import com.google.gerrit.server.submit.SubmoduleException;
 import com.google.gerrit.server.submit.SubmoduleOp;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -102,7 +101,7 @@
         log.error(message);
         throw new CannotRenameProjectException(message);
       }
-    } catch (PermissionBackendException | OrmException | RestApiException e) {
+    } catch (PermissionBackendException | RestApiException e) {
       throw new CannotRenameProjectException(e);
     }
   }
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 a837267..2ed4ad5 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.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ServerInitiated;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.AccountsUpdate;
@@ -28,26 +27,19 @@
 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.NotesMigration;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
 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.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Stream;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -58,69 +50,37 @@
 public class DatabaseRenameHandler {
   private static final Logger log = LoggerFactory.getLogger(DatabaseRenameHandler.class);
 
-  private final SchemaFactory<ReviewDb> schemaFactory;
-  private final ChangeNotes.Factory schemaFactoryNoteDb;
+  private final ChangeNotes.Factory schemaFactory;
   private final GitRepositoryManager repoManager;
   private final Provider<InternalAccountQuery> accountQueryProvider;
   private final Provider<AccountsUpdate> accountsUpdateProvider;
-  private NotesMigration migration;
+
+  private Project.NameKey oldProjectKey;
 
   @Inject
   public DatabaseRenameHandler(
-      SchemaFactory<ReviewDb> schemaFactory,
-      ChangeNotes.Factory schemaFactoryNoteDb,
+      ChangeNotes.Factory schemaFactory,
       GitRepositoryManager repoManager,
-      NotesMigration migration,
       Provider<InternalAccountQuery> accountQueryProvider,
       @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
     this.accountQueryProvider = accountQueryProvider;
     this.schemaFactory = schemaFactory;
-    this.schemaFactoryNoteDb = schemaFactoryNoteDb;
     this.repoManager = repoManager;
     this.accountsUpdateProvider = accountsUpdateProvider;
-    this.migration = migration;
   }
 
-  public List<Change.Id> getChangeIds(Project.NameKey oldProjectKey)
-      throws OrmException, IOException {
+  public List<Change.Id> getChangeIds(Project.NameKey oldProjectKey) throws IOException {
     log.debug("Starting to retrieve changes from the DB for project {}", oldProjectKey.get());
-    ReviewDb db = schemaFactory.open();
-    return (isNoteDb())
-        ? getChangeIdsFromNoteDb(oldProjectKey, db)
-        : getChangeIdsFromReviewDb(oldProjectKey, db);
-  }
+    this.oldProjectKey = oldProjectKey;
 
-  private List<Change.Id> getChangeIdsFromReviewDb(Project.NameKey oldProjectKey, ReviewDb db)
-      throws OrmException {
-    List<Change.Id> changeIds = new ArrayList<>();
-    Connection conn = ((JdbcSchema) db).getConnection();
-    String query =
-        "select change_id from changes where dest_project_name ='" + oldProjectKey.get() + "';";
-    try (Statement stmt = conn.createStatement();
-        ResultSet changes = stmt.executeQuery(query)) {
-      while (changes != null && changes.next()) {
-        Change.Id changeId = new Change.Id(changes.getInt(1));
-        changeIds.add(changeId);
-      }
-      log.debug(
-          "Number of changes in reviewDb related to project {} are {}",
-          oldProjectKey.get(),
-          changeIds.size());
-      return changeIds;
-    } catch (SQLException e) {
-      throw new OrmException(e);
-    }
-  }
-
-  private List<Change.Id> getChangeIdsFromNoteDb(Project.NameKey oldProjectKey, ReviewDb db)
-      throws IOException {
     List<Change.Id> changeIds = new ArrayList<>();
     Stream<ChangeNotesResult> changes =
-        schemaFactoryNoteDb.scan(repoManager.openRepository(oldProjectKey), db, oldProjectKey);
+        schemaFactory.scan(repoManager.openRepository(oldProjectKey), oldProjectKey);
     Iterator<ChangeNotesResult> iterator = changes.iterator();
     while (iterator.hasNext()) {
       ChangeNotesResult change = iterator.next();
-      changeIds.add(change.id());
+      Change.Id changeId = change.id();
+      changeIds.add(changeId);
     }
     log.debug(
         "Number of changes in noteDb related to project {} are {}",
@@ -129,101 +89,21 @@
     return changeIds;
   }
 
-  private boolean isNoteDb() {
-    return migration.disableChangeReviewDb();
-  }
-
   public List<Change.Id> rename(
-      List<Change.Id> changes,
-      Project.NameKey oldProjectKey,
-      Project.NameKey newProjectKey,
-      ProgressMonitor pm)
-      throws OrmException, RenameRevertException {
-    pm.beginTask("Updating changes in the database");
-    ReviewDb db = schemaFactory.open();
-    return (isNoteDb())
-        ? renameInNoteDb(changes, oldProjectKey, newProjectKey)
-        : renameInReviewDb(changes, oldProjectKey, newProjectKey, db);
-  }
-
-  private List<Change.Id> renameInReviewDb(
-      List<Change.Id> changes,
-      Project.NameKey oldProjectKey,
-      Project.NameKey newProjectKey,
-      ReviewDb db)
-      throws OrmException {
-    Connection conn = ((JdbcSchema) db).getConnection();
-    try (Statement stmt = conn.createStatement()) {
-      conn.setAutoCommit(false);
-      try {
-        try {
-          log.debug("Updating the changes in reviewDb related to project {}", oldProjectKey.get());
-          for (Change.Id cd : changes) {
-            stmt.addBatch(
-                "update changes set dest_project_name='"
-                    + newProjectKey.get()
-                    + "' where change_id ="
-                    + cd.id
-                    + ";");
-          }
-          stmt.executeBatch();
-          conn.commit();
-        } catch (SQLException e) {
-          throw new OrmException(e);
-        }
-        updateWatchEntries(oldProjectKey, newProjectKey);
-        log.debug(
-            "Successfully updated the changes in reviewDb related to project {}",
-            oldProjectKey.get());
-        return changes;
-      } catch (OrmException e) {
-        try {
-          log.error(
-              "Failed to update changes in reviewDb for project {}, exception caught: {}. Rolling back the operation.",
-              oldProjectKey.get(),
-              e.toString());
-          conn.rollback();
-        } catch (SQLException revertEx) {
-          log.error(
-              "Failed to rollback changes in reviewDb from project {} to project {}, exception caught: {}",
-              newProjectKey.get(),
-              oldProjectKey.get(),
-              revertEx.toString());
-          throw new RenameRevertException(revertEx, e);
-        }
-        try {
-          updateWatchEntries(newProjectKey, oldProjectKey);
-        } catch (OrmException revertEx) {
-          log.error(
-              "Failed to update watched changes in reviewDb from project {} to project {}, exception caught: {}",
-              newProjectKey.get(),
-              oldProjectKey.get(),
-              revertEx.toString());
-          throw new RenameRevertException(revertEx, e);
-        }
-        throw e;
-      } finally {
-        conn.setAutoCommit(true);
-      }
-    } catch (SQLException e) {
-      throw new OrmException(e);
-    }
-  }
-
-  private List<Change.Id> renameInNoteDb(
-      List<Change.Id> changes, Project.NameKey oldProjectKey, Project.NameKey newProjectKey)
-      throws OrmException {
+      List<Change.Id> changes, Project.NameKey newProjectKey, Optional<ProgressMonitor> opm)
+      throws RenameRevertException, IOException, ConfigInvalidException {
+    opm.ifPresent(pm -> pm.beginTask("Updating changes in the database"));
     log.debug("Updating the changes in noteDb related to project {}", oldProjectKey.get());
     try {
-      updateWatchEntries(oldProjectKey, newProjectKey);
-    } catch (OrmException e) {
+      updateWatchEntries(newProjectKey);
+    } catch (Exception e) {
       log.error(
           "Failed to update changes in noteDb for project {}, exception caught: {}. Rolling back the operation.",
           oldProjectKey.get(),
           e.toString());
       try {
-        updateWatchEntries(newProjectKey, oldProjectKey);
-      } catch (OrmException revertEx) {
+        updateWatchEntries(newProjectKey);
+      } catch (Exception revertEx) {
         log.error(
             "Failed to rollback changes in noteDb from project {} to project {}, exception caught: {}",
             newProjectKey.get(),
@@ -239,8 +119,8 @@
     return changes;
   }
 
-  private void updateWatchEntries(Project.NameKey oldProjectKey, Project.NameKey newProjectKey)
-      throws OrmException {
+  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 =
@@ -274,14 +154,14 @@
                 a.getUserName(),
                 newProjectKey.get(),
                 e);
-            throw new OrmException(e);
+            throw e;
           } catch (IOException e) {
             log.error(
                 "Updating watch entry for user {} in project {} failed.",
                 a.getUserName(),
                 newProjectKey.get(),
                 e);
-            throw new OrmException(e);
+            throw e;
           }
         }
       }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java
index ed12dc3..f75b784 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/database/IndexUpdateHandler.java
@@ -17,17 +17,14 @@
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.renameproject.Configuration;
 import com.googlesource.gerrit.plugins.renameproject.monitor.ProgressMonitor;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -40,30 +37,27 @@
 public class IndexUpdateHandler {
   private static final Logger log = LoggerFactory.getLogger(IndexUpdateHandler.class);
 
-  private final SchemaFactory<ReviewDb> schemaFactory;
   private final ChangeIndexer indexer;
   private final Configuration config;
 
   @Inject
-  public IndexUpdateHandler(
-      SchemaFactory<ReviewDb> schemaFactory, ChangeIndexer indexer, Configuration config) {
-    this.schemaFactory = schemaFactory;
+  public IndexUpdateHandler(ChangeIndexer indexer, Configuration config) {
     this.indexer = indexer;
     this.config = config;
   }
 
   public void updateIndex(
-      List<Change.Id> changeIds, Project.NameKey newProjectKey, ProgressMonitor pm)
+      List<Change.Id> changeIds, Project.NameKey newProjectKey, Optional<ProgressMonitor> opm)
       throws InterruptedException {
     log.debug("Starting to index {} change(s).", changeIds.size());
     ExecutorService executor =
         Executors.newFixedThreadPool(
             config.getIndexThreads(),
             new ThreadFactoryBuilder().setNameFormat("Rename-Index-%d").build());
-    pm.beginTask("Indexing changes", changeIds.size());
+    opm.ifPresent(pm -> pm.beginTask("Indexing changes", changeIds.size()));
     List<Callable<Boolean>> callableTasks = new ArrayList<>(changeIds.size());
     for (Change.Id id : changeIds) {
-      callableTasks.add(new IndexTask(id, newProjectKey, pm));
+      callableTasks.add(new IndexTask(id, newProjectKey, opm));
     }
     List<Future<Boolean>> tasksCompleted = executor.invokeAll(callableTasks);
     executor.shutdown();
@@ -90,9 +84,10 @@
 
     private Change.Id changeId;
     private Project.NameKey newProjectKey;
-    private ProgressMonitor monitor;
+    private Optional<ProgressMonitor> monitor;
 
-    IndexTask(Change.Id changeId, Project.NameKey newProjectKey, ProgressMonitor monitor) {
+    IndexTask(
+        Change.Id changeId, Project.NameKey newProjectKey, Optional<ProgressMonitor> monitor) {
       this.changeId = changeId;
       this.newProjectKey = newProjectKey;
       this.monitor = monitor;
@@ -100,14 +95,9 @@
 
     @Override
     public Boolean call() throws Exception {
-      try (ReviewDb db = schemaFactory.open()) {
-        indexer.index(db, newProjectKey, changeId);
-        monitor.update(1);
-        return Boolean.TRUE;
-      } catch (OrmException | IOException e) {
-        log.error("Failed to reindex change {} from index, Cause: {}", changeId, e);
-        throw e;
-      }
+      indexer.index(newProjectKey, changeId);
+      monitor.ifPresent(monitor -> monitor.update(1));
+      return Boolean.TRUE;
     }
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java b/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java
index 29a76d8..1572d53 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/renameproject/fs/FilesystemRenameHandler.java
@@ -26,6 +26,7 @@
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.util.Comparator;
+import java.util.Optional;
 import java.util.stream.Stream;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.Repository;
@@ -45,12 +46,12 @@
   }
 
   public void rename(
-      Project.NameKey oldProjectKey, Project.NameKey newProjectKey, ProgressMonitor pm)
+      Project.NameKey oldProjectKey, Project.NameKey newProjectKey, Optional<ProgressMonitor> opm)
       throws IOException, RepositoryNotFoundException {
     Repository repository = repoManager.openRepository(oldProjectKey);
     File repoFile = repository.getDirectory();
     RepositoryCache.close(repository);
-    pm.beginTask("Renaming git repository");
+    opm.ifPresent(pm -> pm.beginTask("Renaming git repository"));
     renameGitRepository(repoFile, newProjectKey, oldProjectKey);
   }
 
diff --git a/src/main/resources/Documentation/rest-api-rename.md b/src/main/resources/Documentation/rest-api-rename.md
new file mode 100644
index 0000000..70ecf15
--- /dev/null
+++ b/src/main/resources/Documentation/rest-api-rename.md
@@ -0,0 +1,55 @@
+@PLUGIN@ - /@PLUGIN@/ REST API
+===================================
+
+This page describes the REST endpoint that is added by the @PLUGIN@
+plugin.
+
+Please also take note of the general information on the
+[REST API](../../../Documentation/rest-api.html).
+
+This API implements a REST equivalent of the Ssh rename-project command.
+For more information, refer to:
+* [Ssh rename-project command](cmd-rename.md)
+------------------------------------------
+
+REQUEST
+-------
+```
+POST /projects/project-1/@PLUGIN@~rename HTTP/1.1
+  {
+    "name" : "project-2",
+  }
+```
+to rename project-1 to project-2.
+
+By default, if project-1 has more than 5000 changes, the rename procedure will be cancelled as it
+can take longer time and can degrade in performance in that time frame.
+
+To rename a project with more than 5000 changes, the following request is needed:
+```
+POST /projects/project-1/@PLUGIN@~rename HTTP/1.1
+  {
+    "name" : "project-2",
+    "continueWithRename" : "true"
+  }
+```
+
+RESPONSE
+--------
+If rename succeeded:
+
+```
+HTTP/1.1 200 OK
+```
+
+If rename was cancelled due to user's intent to not proceed when the number of changes exceeds the
+warning limit of 5000 changes:
+
+```
+HTTP/1.1 204 No Content
+```
+
+ACCESS
+------
+Same as ssh version of the command, caller must be a member of a group that is granted the
+'Rename Project' (provided by this plugin) or 'Administrate Server' capabilities.
diff --git a/src/test/README.md b/src/test/README.md
new file mode 100644
index 0000000..f6147ff
--- /dev/null
+++ b/src/test/README.md
@@ -0,0 +1,16 @@
+# How to e2e-test this plugin
+
+Consider the
+[instructions](https://gerrit-review.googlesource.com/Documentation/dev-e2e-tests.html)
+on how to use Gerrit core's Gatling framework, to run non-core test
+scenarios such as this plugin one below:
+
+```
+  $ sbt "gatling:testOnly com.googlesource.gerrit.plugins.renameproject.scenarios.RenameProject"
+```
+
+Scenario scala source files and their companion json resource ones are
+stored under the usual src/test directories. That structure follows the
+scala package one from the scenario classes. The core framework expects
+such a directory structure for both the scala and resources (json data)
+files.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java
index c0ae8f0..7df0e60 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RenameIT.java
@@ -19,15 +19,18 @@
 import com.google.common.cache.Cache;
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit.Result;
+import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
 import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.reviewdb.client.Change.Id;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.Project.NameKey;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.renameproject.RenameProject.Input;
 import java.util.List;
 import javax.inject.Named;
 import org.eclipse.jgit.junit.TestRepository;
@@ -44,6 +47,8 @@
   private static final String NEW_PROJECT_NAME = "newProject";
   private static final String CACHE_NAME = "changeid_project";
 
+  @Inject private RequestScopeOperations requestScopeOperations;
+
   @Inject
   @Named(CACHE_NAME)
   private Cache<Id, String> changeIdProjectCache;
@@ -95,12 +100,13 @@
   @Test
   @UseLocalDisk
   public void testRenameSubscribedFail() throws Exception {
-    NameKey superProject = createProject("super-project");
+    NameKey superProject = createProjectOverAPI("super-project", null, true, null);
     TestRepository<?> superRepo = cloneProject(superProject);
-    NameKey subProject = createProject("subscribed-to-project");
+    NameKey subProject = createProjectOverAPI("subscribed-to-project", null, true, null);
     SubmoduleUtil.allowSubmoduleSubscription(
         metaDataUpdateFactory,
         projectCache,
+        projectConfigFactory,
         subProject,
         "refs/heads/master",
         superProject,
@@ -145,4 +151,39 @@
 
     assertThat(changeIdProjectCache.getIfPresent(changeID)).isNull();
   }
+
+  @Test
+  @UseLocalDisk
+  public void testRenameViaHttpSuccessful() throws Exception {
+    createChange();
+    RestResponse r = renameProjectTo(NEW_PROJECT_NAME);
+    r.assertOK();
+
+    ProjectState projectState = projectCache.get(Project.nameKey(NEW_PROJECT_NAME));
+    assertThat(projectState).isNotNull();
+    assertThat(queryProvider.get().byProject(project)).isEmpty();
+    assertThat(queryProvider.get().byProject(Project.nameKey(NEW_PROJECT_NAME))).isNotEmpty();
+  }
+
+  @Test
+  @UseLocalDisk
+  public void testRenameViaHttpWithEmptyNewName() throws Exception {
+    createChange();
+    String newProjectName = "";
+    RestResponse r = renameProjectTo(newProjectName);
+    r.assertBadRequest();
+
+    ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
+    assertThat(projectState).isNull();
+  }
+
+  private RestResponse renameProjectTo(String newName) throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    sender.clear();
+    String endPoint = "/projects/" + project.get() + "/" + PLUGIN_NAME + "~rename";
+    Input i = new Input();
+    i.name = newName;
+    i.continueWithRename = true;
+    return adminRestSession.post(endPoint, i);
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java
index d060414..f77d80e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/renameproject/RevertRenameProjectTest.java
@@ -30,6 +30,7 @@
 import com.googlesource.gerrit.plugins.renameproject.RenameProject.Step;
 import com.googlesource.gerrit.plugins.renameproject.monitor.ProgressMonitor;
 import java.util.List;
+import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -45,7 +46,7 @@
   private RevertRenameProject revertRenameProject;
   private Project.NameKey oldProjectKey;
   private Project.NameKey newProjectKey;
-  private ProgressMonitor pm;
+  private Optional<ProgressMonitor> pm;
   private ProjectResource oldRsrc;
 
   @Before
@@ -56,7 +57,7 @@
     oldProjectKey = project;
     newProjectKey = new Project.NameKey(NEW_PROJECT_NAME);
 
-    pm = Mockito.mock(ProgressMonitor.class);
+    pm = Optional.of(Mockito.mock(ProgressMonitor.class));
 
     oldRsrc = Mockito.mock(ProjectResource.class);
     when(oldRsrc.getNameKey()).thenReturn(oldProjectKey);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java b/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java
index e6fcdd1..68515c4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/renameproject/SubmoduleUtil.java
@@ -45,6 +45,7 @@
   static void allowSubmoduleSubscription(
       Server metaDataUpdateFactory,
       ProjectCache projectCache,
+      ProjectConfig.Factory projectConfigFactory,
       NameKey sub,
       String subBranch,
       NameKey superName,
@@ -54,7 +55,7 @@
     try (MetaDataUpdate md = metaDataUpdateFactory.create(sub)) {
       md.setMessage("Added superproject subscription");
       SubscribeSection s;
-      ProjectConfig pc = ProjectConfig.read(md);
+      ProjectConfig pc = projectConfigFactory.read(md);
       if (pc.getSubscribeSections().containsKey(superName)) {
         s = pc.getSubscribeSections().get(superName);
       } else {
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject-body.json b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject-body.json
new file mode 100644
index 0000000..fd49984
--- /dev/null
+++ b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject-body.json
@@ -0,0 +1,3 @@
+{
+  "name": "${project}"
+}
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.json b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.json
new file mode 100644
index 0000000..6be790c
--- /dev/null
+++ b/src/test/resources/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.json
@@ -0,0 +1,6 @@
+[
+  {
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/_PROJECT/rename-project~rename",
+    "project": "_RENAMED"
+  }
+]
diff --git a/src/test/scala/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.scala b/src/test/scala/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.scala
new file mode 100644
index 0000000..36665ee
--- /dev/null
+++ b/src/test/scala/com/googlesource/gerrit/plugins/renameproject/scenarios/RenameProject.scala
@@ -0,0 +1,55 @@
+// Copyright (C) 2020 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.scenarios
+
+import com.google.gerrit.scenarios.{CreateProject, DeleteProject, GerritSimulation}
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+
+import scala.concurrent.duration._
+
+class RenameProject extends GerritSimulation {
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+  private val projectName = className
+  private val renamedTo = uniqueName
+
+  override def replaceOverride(in: String): String = {
+    val next = replaceKeyWith("_project", projectName, in)
+    replaceKeyWith("_renamed", renamedTo, next)
+  }
+
+  private val createProject = new CreateProject(projectName)
+  private val deleteProject = new DeleteProject(renamedTo)
+
+  private val test: ScenarioBuilder = scenario(uniqueName)
+    .feed(data)
+    .exec(httpRequest.body(ElFileBody(body)).asJson)
+
+  setUp(
+    createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
+      atOnceUsers(1)
+    ),
+    test.inject(
+      nothingFor(stepWaitTime(this) seconds),
+      atOnceUsers(1)
+    ),
+    deleteProject.test.inject(
+      nothingFor(stepWaitTime(deleteProject) seconds),
+      atOnceUsers(1)
+    ),
+  ).protocols(httpProtocol)
+}