Update to support Gerrit 3.0.9

Change-Id: I9c959d1e1a64866e739290b9bd167f903fe1ef5d
diff --git a/.gitignore b/.gitignore
index ec6ea01..4da386b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
 /buck-out
 *.iml
 /.apt_generated/
+/.apt_generated_tests/
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 40e022d..538e9eb 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -28,6 +28,7 @@
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
@@ -86,6 +87,7 @@
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
 org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
 org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
 org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
@@ -123,4 +125,5 @@
 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
 org.eclipse.jdt.core.compiler.processAnnotations=enabled
+org.eclipse.jdt.core.compiler.release=disabled
 org.eclipse.jdt.core.compiler.source=1.8
diff --git a/BUILD b/BUILD
index 9acc76b..0e08d92 100644
--- a/BUILD
+++ b/BUILD
@@ -1,11 +1,16 @@
-load("//tools/bzl:plugin.bzl", "PLUGIN_DEPS", "PLUGIN_TEST_DEPS", "gerrit_plugin")
+load(
+    "//tools/bzl:plugin.bzl",
+    "PLUGIN_DEPS",
+    "PLUGIN_TEST_DEPS",
+    "gerrit_plugin",
+)
 
 gerrit_plugin(
     name = "batch",
     srcs = glob(["src/main/java/**/*.java"]),
     manifest_entries = [
         "Gerrit-PluginName: batch",
-        "Gerrit-ApiVersion: 2.16",
+        "Gerrit-ApiVersion: 3.0.0",
         "Implementation-Title: Batch Plugin",
         "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/batch",
         "Gerrit-Module: com.googlesource.gerrit.plugins.batch.Module",
diff --git a/WORKSPACE b/WORKSPACE
index 4890441..ac604b5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "22b242638bd36017790d94ffcc18a57f223bda2f",
+    commit = "15eae2ee5cd524a204bd62c3d59bfd0ce86916ec",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/pom.xml b/pom.xml
index ae82977..a9141b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
   <groupId>com.googlesource.gerrit.plugins.batch</groupId>
   <artifactId>batch</artifactId>
   <packaging>jar</packaging>
-  <version>2.16.11.1</version>
+  <version>3.0.9</version>
   <name>batch</name>
 
   <properties>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/BatchSubmitter.java b/src/main/java/com/googlesource/gerrit/plugins/batch/BatchSubmitter.java
index 3893d3c..abaad8b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/BatchSubmitter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/BatchSubmitter.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -39,7 +38,6 @@
 import com.google.gerrit.server.util.RefUpdater;
 import com.google.gerrit.server.util.RequestScopePropagator;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.batch.exception.NoSuchBatchException;
 import java.io.IOException;
@@ -47,13 +45,14 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
 
 public class BatchSubmitter {
   private static final FluentLogger log = FluentLogger.forEnclosingClass();
 
-  protected final ReviewDb db;
   protected final GitRepositoryManager repoManager;
   protected final RefUpdater refUpdater;
   protected final RequestScopePropagator requestScopePropagator;
@@ -68,7 +67,6 @@
 
   @Inject
   BatchSubmitter(
-      ReviewDb db,
       GitRepositoryManager repoManager,
       RefUpdater refUpdater,
       RequestScopePropagator requestScopePropagator,
@@ -80,7 +78,6 @@
       PatchSetUtil psUtil,
       BatchStore store,
       BatchRemover remover) {
-    this.db = db;
     this.repoManager = repoManager;
     this.refUpdater = refUpdater;
     this.requestScopePropagator = requestScopePropagator;
@@ -96,7 +93,7 @@
 
   public Batch submit(String id)
       throws IOException, IllegalStateException, NoSuchBatchException, NoSuchProjectException,
-          OrmException, RestApiException, UpdateException, PermissionBackendException {
+          RestApiException, UpdateException, PermissionBackendException {
     Batch batch = store.read(id);
     if (batch.state == Batch.State.OPEN) {
       throw new IllegalStateException("Cannot submit batch " + id + " in state " + batch.state);
@@ -121,8 +118,8 @@
   }
 
   private void submit(Batch batch)
-      throws IOException, OrmException, NoSuchProjectException, RepositoryNotFoundException,
-          RestApiException, UpdateException, PermissionBackendException {
+      throws IOException, NoSuchProjectException, RepositoryNotFoundException, RestApiException,
+          UpdateException, PermissionBackendException {
     for (Batch.Destination dest : batch.listDestinations()) {
       updateRef(dest);
       closeChanges(dest);
@@ -137,8 +134,8 @@
   }
 
   private void closeChanges(Batch.Destination dest)
-      throws IOException, OrmException, RepositoryNotFoundException, RestApiException,
-          UpdateException, PermissionBackendException {
+      throws IOException, RepositoryNotFoundException, RestApiException, UpdateException,
+          PermissionBackendException {
     if (dest.changes != null) {
       for (Batch.Change change : dest.changes) {
         closeChange(change.toPatchSetId(), dest.sha1);
@@ -147,12 +144,12 @@
   }
 
   private void closeChange(PatchSet.Id psId, String sha1)
-      throws IOException, OrmException, RepositoryNotFoundException, RestApiException,
-          UpdateException, PermissionBackendException {
+      throws IOException, RepositoryNotFoundException, RestApiException, UpdateException,
+          PermissionBackendException {
     ChangeNotes changeNotes = notesFactory.createChecked(psId.getParentKey());
-    permissionBackend.user(user).database(db).change(changeNotes).check(ChangePermission.READ);
+    permissionBackend.user(user).change(changeNotes).check(ChangePermission.READ);
     Change change = changeNotes.getChange();
-    PatchSet ps = psUtil.get(db, changeNotes, psId);
+    PatchSet ps = psUtil.get(changeNotes, psId);
     if (change == null || ps == null) {
       log.atSevere().log("%s is missing", psId);
       return;
@@ -169,11 +166,13 @@
             TraceContext.open()
                 .addTag(RequestId.Type.SUBMISSION_ID, new RequestId(change.getId().toString()));
         Repository repo = repoManager.openRepository(project);
-        BatchUpdate bu = batchUpdateFactory.create(db, project, user, TimeUtil.nowTs());
+        BatchUpdate bu = batchUpdateFactory.create(project, user, TimeUtil.nowTs());
         ObjectInserter ins = repo.newObjectInserter();
         ObjectReader reader = ins.newReader();
         RevWalk walk = new RevWalk(reader)) {
-      bu.setRepository(repo, walk, ins).updateChangesInParallel();
+      Ref destRef = repo.getRefDatabase().exactRef(destination.get());
+      RevCommit newTip = walk.parseCommit(destRef.getObjectId());
+      bu.setRepository(repo, walk, ins);
       bu.setRefLogMessage("merged (batch submit)");
       bu.addOp(
           psId.getParentKey(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java
index b997d75..db1809e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ListBatches.java
@@ -20,9 +20,8 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.json.OutputFormat;
 import com.google.gson.reflect.TypeToken;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.batch.query.BatchQueryBuilder;
 import java.io.BufferedWriter;
@@ -58,8 +57,7 @@
     this.store = store;
   }
 
-  public void display(OutputStream displayOutputStream)
-      throws IOException, OrmException, QueryParseException {
+  public void display(OutputStream displayOutputStream) throws IOException, QueryParseException {
     try {
       PrintWriter stdout =
           new PrintWriter(new BufferedWriter(new OutputStreamWriter(displayOutputStream, UTF_8)));
@@ -76,7 +74,7 @@
     }
   }
 
-  public List<Batch> getBatches() throws IOException, OrmException, QueryParseException {
+  public List<Batch> getBatches() throws IOException, QueryParseException {
     Predicate<Batch> pred = null;
     if (query != null) {
       pred = queryBuilder.parse(Joiner.on(" ").join(query));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/cli/PatchSetArgument.java b/src/main/java/com/googlesource/gerrit/plugins/batch/cli/PatchSetArgument.java
index da157f8..4feb2fa 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/cli/PatchSetArgument.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/cli/PatchSetArgument.java
@@ -16,7 +16,6 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -24,14 +23,12 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
 public class PatchSetArgument {
   public static class Factory {
     protected final PermissionBackend permissionBackend;
     protected final ChangeNotes.Factory notesFactory;
-    protected final ReviewDb reviewDb;
     protected final PatchSetUtil psUtil;
     protected final CurrentUser user;
 
@@ -39,12 +36,10 @@
     protected Factory(
         ChangeNotes.Factory notesFactory,
         PermissionBackend permissionBackend,
-        ReviewDb reviewDb,
         PatchSetUtil psUtil,
         CurrentUser user) {
       this.notesFactory = notesFactory;
       this.permissionBackend = permissionBackend;
-      this.reviewDb = reviewDb;
       this.psUtil = psUtil;
       this.user = user;
     }
@@ -53,19 +48,12 @@
       try {
         PatchSet.Id patchSetId = parsePatchSet(token);
         ChangeNotes changeNotes = notesFactory.createChecked(patchSetId.getParentKey());
-        permissionBackend
-            .user(user)
-            .database(reviewDb)
-            .change(changeNotes)
-            .check(ChangePermission.READ);
-        return new PatchSetArgument(
-            changeNotes.getChange(), psUtil.get(reviewDb, changeNotes, patchSetId));
+        permissionBackend.user(user).change(changeNotes).check(ChangePermission.READ);
+        return new PatchSetArgument(changeNotes.getChange(), psUtil.get(changeNotes, patchSetId));
       } catch (PermissionBackendException | AuthException e) {
         throw new IllegalArgumentException("database error", e);
       } catch (UnloggedFailure e) {
         throw new IllegalArgumentException(e.getMessage(), e);
-      } catch (OrmException e) {
-        throw new IllegalArgumentException("database error", e);
       }
     }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/query/BatchQueryBuilder.java b/src/main/java/com/googlesource/gerrit/plugins/batch/query/BatchQueryBuilder.java
index 160fde6..0875963 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/query/BatchQueryBuilder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/query/BatchQueryBuilder.java
@@ -31,7 +31,7 @@
 import org.eclipse.jgit.lib.Config;
 
 /** Parses a query string meant to be applied to batch objects. */
-public class BatchQueryBuilder extends QueryBuilder<Batch> {
+public class BatchQueryBuilder extends QueryBuilder<Batch, BatchQueryBuilder> {
   public abstract static class SimplePredicate extends OperatorPredicate<Batch>
       implements Matchable<Batch> {
     public SimplePredicate(String op, String val) {
@@ -65,7 +65,7 @@
       PluginConfigFactory cfgFactory,
       BatchStore store,
       @PluginName String pluginName) {
-    super(mydef);
+    super(mydef, null);
     this.projectCache = projectCache;
     this.cfgFactory = cfgFactory;
     this.store = store;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/DeleteCommand.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/DeleteCommand.java
index aeec6b7..10d1f17 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/DeleteCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/DeleteCommand.java
@@ -15,7 +15,7 @@
 
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/MergeChangeCommand.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/MergeChangeCommand.java
index 37b4428..64ccacc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/MergeChangeCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/MergeChangeCommand.java
@@ -13,12 +13,11 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.batch.ssh;
 
+import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.OutputFormat;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.NoSuchRefException;
 import com.google.gerrit.sshd.CommandMetaData;
@@ -84,7 +83,6 @@
   @Inject protected PatchSetArgument.Factory patchSetArgumentFactory;
   @Inject protected MergeBranch.Factory mergeBranchFactory;
   @Inject protected BatchCloser batchCloser;
-  @Inject protected ReviewDb db;
   @Inject protected GitRepositoryManager repoManager;
   protected Map<PatchSet.Id, List<ObjectId>> parentsByPsarg = new HashMap<>();
 
@@ -151,7 +149,7 @@
   protected ObjectId getTip(Branch.NameKey branch)
       throws IOException, NoSuchRefException, RepositoryNotFoundException {
     try (Repository repo = repoManager.openRepository(branch.getParentKey())) {
-      Ref ref = repo.getRefDatabase().getRef(branch.get());
+      Ref ref = repo.getRefDatabase().exactRef(branch.get());
       if (ref == null) {
         throw new NoSuchRefException(branch.toString());
       }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SubmitCommand.java b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SubmitCommand.java
index 1737818..a753c36 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SubmitCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/ssh/SubmitCommand.java
@@ -14,7 +14,7 @@
 
 package com.googlesource.gerrit.plugins.batch.ssh;
 
-import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/util/MergeBranch.java b/src/main/java/com/googlesource/gerrit/plugins/batch/util/MergeBranch.java
index 5d9323e..bcec3a7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/util/MergeBranch.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/util/MergeBranch.java
@@ -89,7 +89,7 @@
       throws IOException, NoSuchRefException, RepositoryNotFoundException, IntegrationException,
           BadRequestException {
     try (Repository repo = repoManager.openRepository(projectName)) {
-      Ref destRef = repo.getRefDatabase().getRef(destName);
+      Ref destRef = repo.getRefDatabase().exactRef(destName);
       if (destRef == null) {
         throw new NoSuchRefException(destName);
       }
diff --git a/test/test_batch_merge.sh b/test/test_batch_merge.sh
index cb7bc67..d0ca20f 100755
--- a/test/test_batch_merge.sh
+++ b/test/test_batch_merge.sh
@@ -66,7 +66,7 @@
 query_by() { echo "$1" | awk '/^ *'"$2"':/{print $2}' ; } # qchange key > val
 
 get_change_num() { # < gerrit_push_response > changenum
-    local url=$(awk '/New Changes:/ { getline; print $2 }')
+    local url=$(awk '$NF ~ /\[NEW\]/ { print $2 }')
     echo "${url##*\/}" | tr -d -c '[:digit:]'
 }
 
diff --git a/tools/workspace-status.py b/tools/workspace-status.py
new file mode 100755
index 0000000..829f438
--- /dev/null
+++ b/tools/workspace-status.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+# This script will be run by bazel when the build process starts to
+# generate key-value information that represents the status of the
+# workspace. The output should be like
+#
+# KEY1 VALUE1
+# KEY2 VALUE2
+#
+# If the script exits with non-zero code, it's considered as a failure
+# and the output will be discarded.
+
+from __future__ import print_function
+import subprocess
+import sys
+
+CMD = ['git', 'describe', '--always', '--match', 'v[0-9].*', '--dirty']
+
+
+def revision():
+    try:
+        return subprocess.check_output(CMD).strip().decode("utf-8")
+    except OSError as err:
+        print('could not invoke git: %s' % err, file=sys.stderr)
+        sys.exit(1)
+    except subprocess.CalledProcessError as err:
+        print('error using git: %s' % err, file=sys.stderr)
+        sys.exit(1)
+
+
+print("STABLE_BUILD_BATCH_LABEL %s" % revision())