Merge branch 'stable-2.16' into stable-3.0

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

Change-Id: Iaa67cfe9bcf1a8df0f1f98909f2b7bf2d4bc5527
diff --git a/.bazelversion b/.bazelversion
index fd2a018..47b322c 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.1.0
+3.4.1
diff --git a/.factorypath b/.factorypath
new file mode 100644
index 0000000..c1c6e82
--- /dev/null
+++ b/.factorypath
@@ -0,0 +1,3 @@
+<factorypath>
+    <factorypathentry kind="VARJAR" id="M2_REPO/com/google/auto/value/auto-value/1.7/auto-value-1.7.jar" enabled="true" runInBatchMode="false"/>
+</factorypath>
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..f364456 100644
--- a/BUILD
+++ b/BUILD
@@ -1,15 +1,64 @@
-load("//tools/bzl:plugin.bzl", "PLUGIN_DEPS", "PLUGIN_TEST_DEPS", "gerrit_plugin")
+load(
+    "//tools/bzl:plugin.bzl",
+    "PLUGIN_DEPS",
+    "PLUGIN_TEST_DEPS",
+    "gerrit_plugin",
+)
+
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+
+java_plugin(
+    name = "auto-annotation-plugin",
+    processor_class = "com.google.auto.value.processor.AutoAnnotationProcessor",
+    deps = [
+        "@auto-value-annotations//jar",
+        "@auto-value//jar",
+    ],
+)
+
+java_plugin(
+    name = "auto-value-plugin",
+    processor_class = "com.google.auto.value.processor.AutoValueProcessor",
+    deps = [
+        "@auto-value-annotations//jar",
+        "@auto-value//jar",
+    ],
+)
+
+java_library(
+    name = "auto-value",
+    exported_plugins = [
+        ":auto-annotation-plugin",
+        ":auto-value-plugin",
+    ],
+    visibility = ["//visibility:public"],
+    exports = ["@auto-value//jar"],
+)
+
+java_library(
+    name = "auto-value-annotations",
+    exported_plugins = [
+        ":auto-annotation-plugin",
+        ":auto-value-plugin",
+    ],
+    visibility = ["//visibility:public"],
+    exports = ["@auto-value-annotations//jar"],
+)
 
 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",
         "Gerrit-SshModule: com.googlesource.gerrit.plugins.batch.ssh.SshModule",
     ],
     resources = glob(["src/main/resources/**/*"]),
+    deps = [
+        ":auto-value",
+        ":auto-value-annotations"
+    ],
 )
diff --git a/WORKSPACE b/WORKSPACE
index 3bae714..d8bb74a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,24 +3,17 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "8ad33887665f4f6adf7cb465a03f6bd81b95e01d",
+    commit = "7a9ae377b519934c87184cc05845663ed708b69c",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
-# Release Plugin API
 load(
     "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
     "gerrit_api",
 )
 
-# Snapshot Plugin API
-#load(
-#    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
-#    "gerrit_api_maven_local",
-#)
-
-# Load release Plugin API
 gerrit_api()
 
-# Load snapshot Plugin API
-#gerrit_api_maven_local()
+load("//:external_plugin_deps.bzl", "external_plugin_deps")
+
+external_plugin_deps()
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
new file mode 100644
index 0000000..8d7be89
--- /dev/null
+++ b/external_plugin_deps.bzl
@@ -0,0 +1,16 @@
+load("//tools/bzl:maven_jar.bzl", "maven_jar")
+
+def external_plugin_deps():
+    AUTO_VALUE_VERSION = "1.6.5"
+
+    maven_jar(
+        name = "auto-value",
+        artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
+        sha1 = "816872c85048f36a67a276ef7a49cc2e4595711c",
+    )
+
+    maven_jar(
+        name = "auto-value-annotations",
+        artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
+        sha1 = "c3dad10377f0e2242c9a4b88e9704eaf79103679",
+    )
diff --git a/pom.xml b/pom.xml
index ae82977..b1ea7c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,12 +22,13 @@
   <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>
     <Gerrit-ApiType>plugin</Gerrit-ApiType>
     <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion>
+    <auto-value.version>1.6.5</auto-value.version>
   </properties>
 
   <build>
@@ -64,6 +65,13 @@
           <source>1.8</source>
           <target>1.8</target>
           <encoding>UTF-8</encoding>
+          <annotationProcessorPaths>
+            <path>
+              <groupId>com.google.auto.value</groupId>
+              <artifactId>auto-value</artifactId>
+              <version>${auto-value.version}</version>
+            </path>
+          </annotationProcessorPaths>
         </configuration>
       </plugin>
     </plugins>
@@ -76,6 +84,12 @@
       <version>${Gerrit-ApiVersion}</version>
       <scope>provided</scope>
     </dependency>
+
+    <dependency>
+      <groupId>com.google.auto.value</groupId>
+      <artifactId>auto-value-annotations</artifactId>
+      <version>${auto-value.version}</version>
+    </dependency>
   </dependencies>
 
   <repositories>
diff --git a/src/main/java/com/google/gerrit/entities/FileNameKey.java b/src/main/java/com/google/gerrit/entities/FileNameKey.java
new file mode 100644
index 0000000..d415278
--- /dev/null
+++ b/src/main/java/com/google/gerrit/entities/FileNameKey.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2017 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.google.gerrit.entities;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+
+/** An immutable reference to a file in gerrit repo. */
+@AutoValue
+public abstract class FileNameKey implements Comparable<FileNameKey> {
+  public static FileNameKey create(Branch.NameKey branch, String file) {
+    return new AutoValue_FileNameKey(branch, file);
+  }
+
+  public static FileNameKey create() {
+    return new AutoValue_FileNameKey(new Branch.NameKey(new Project.NameKey(null), null), null);
+  }
+
+  public abstract Branch.NameKey branch();
+  public abstract String file();
+
+  @Override
+  public final int compareTo(FileNameKey o) {
+    return file().compareTo(o.file());
+  }
+}
diff --git a/src/main/java/com/google/gerrit/reviewdb/client/File.java b/src/main/java/com/google/gerrit/reviewdb/client/File.java
deleted file mode 100644
index 78e2f84..0000000
--- a/src/main/java/com/google/gerrit/reviewdb/client/File.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2017 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.google.gerrit.reviewdb.client;
-
-import com.google.gwtorm.client.StringKey;
-
-public class File {
-  /** An immutable reference to a file in gerrit repo. */
-  public static class NameKey extends StringKey<Branch.NameKey> {
-    private static final long serialVersionUID = 1L;
-
-    protected Branch.NameKey branch;
-    protected String fileName;
-
-    protected NameKey() {
-      branch = new Branch.NameKey(new Project.NameKey(null), null);
-    }
-
-    public NameKey(Branch.NameKey br, String file) {
-      branch = br;
-      fileName = file;
-    }
-
-    @Override
-    public String get() {
-      return fileName;
-    }
-
-    @Override
-    protected void set(String file) {
-      fileName = file;
-    }
-
-    @Override
-    public Branch.NameKey getParentKey() {
-      return branch;
-    }
-  }
-}
diff --git a/src/main/java/com/google/gerrit/server/git/meta/GitFile.java b/src/main/java/com/google/gerrit/server/git/meta/GitFile.java
index ce9505e..4fe617d 100644
--- a/src/main/java/com/google/gerrit/server/git/meta/GitFile.java
+++ b/src/main/java/com/google/gerrit/server/git/meta/GitFile.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.server.git.meta;
 
 import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.File;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.FileNameKey;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.inject.Inject;
@@ -31,7 +31,7 @@
 /** A GitFile is a text file (UTF8) from a git repository */
 public class GitFile extends VersionedMetaData {
   public interface Factory {
-    GitFile create(@Assisted File.NameKey file);
+    GitFile create(@Assisted FileNameKey file);
   }
 
   protected final MetaDataUpdate.User metaDataUpdateFactory;
@@ -46,11 +46,11 @@
   public GitFile(
       MetaDataUpdate.User metaDataUpdateFactory,
       GitRepositoryManager repos,
-      @Assisted File.NameKey file) {
+      @Assisted FileNameKey file) {
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.repos = repos;
-    this.branch = file.getParentKey();
-    this.file = file.get();
+    this.branch = file.branch();
+    this.file = file.file();
   }
 
   public String read() throws ConfigInvalidException, IOException, NoSuchProjectException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java b/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java
index d582af4..7290148 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/batch/BatchStore.java
@@ -14,7 +14,7 @@
 package com.googlesource.gerrit.plugins.batch;
 
 import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.File;
+import com.google.gerrit.entities.FileNameKey;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -106,7 +106,7 @@
     throw new NoSuchBatchException(id);
   }
 
-  protected File.NameKey getFileNameKey(String id) {
+  protected FileNameKey getFileNameKey(String id) {
     return getFileNameKey(getBranch(id));
   }
 
@@ -114,7 +114,7 @@
     return new Branch.NameKey(project, BATCHES_REF + id);
   }
 
-  protected File.NameKey getFileNameKey(Branch.NameKey branch) {
-    return new File.NameKey(branch, FILE_NAME);
+  protected FileNameKey getFileNameKey(Branch.NameKey branch) {
+    return FileNameKey.create(branch, FILE_NAME);
   }
 }
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..018f524 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;
@@ -53,7 +51,6 @@
 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 +65,6 @@
 
   @Inject
   BatchSubmitter(
-      ReviewDb db,
       GitRepositoryManager repoManager,
       RefUpdater refUpdater,
       RequestScopePropagator requestScopePropagator,
@@ -80,7 +76,6 @@
       PatchSetUtil psUtil,
       BatchStore store,
       BatchRemover remover) {
-    this.db = db;
     this.repoManager = repoManager;
     this.refUpdater = refUpdater;
     this.requestScopePropagator = requestScopePropagator;
@@ -96,7 +91,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 +116,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 +132,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 +142,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 +164,11 @@
             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();
+      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/bzl/maven_jar.bzl b/tools/bzl/maven_jar.bzl
new file mode 100644
index 0000000..ed1504d
--- /dev/null
+++ b/tools/bzl/maven_jar.bzl
@@ -0,0 +1,4 @@
+load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", _gerrit = "GERRIT", _maven_jar = "maven_jar")
+
+maven_jar = _maven_jar
+GERRIT = _gerrit
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())