Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Upgrade bazlets to latest stable-2.16

Change-Id: I9b7ade431e81fc0818b792a51cdb1e85da5cb646
diff --git a/BUILD b/BUILD
index b0ec26b..147b37f 100644
--- a/BUILD
+++ b/BUILD
@@ -33,6 +33,5 @@
     visibility = ["//visibility:public"],
     exports = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
         ":quota__plugin",
-        "@mockito//jar",
     ],
 )
diff --git a/WORKSPACE b/WORKSPACE
index 606b164..7c39d85 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,28 +3,13 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "56c9f4ed6dbe3c88f793851c88389637f3714a5d",
+    commit = "7a9ae377b519934c87184cc05845663ed708b69c",
     #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")
-
-external_plugin_deps()
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
deleted file mode 100644
index 790d564..0000000
--- a/external_plugin_deps.bzl
+++ /dev/null
@@ -1,32 +0,0 @@
-load("//tools/bzl:maven_jar.bzl", "maven_jar")
-
-def external_plugin_deps():
-    maven_jar(
-        name = "mockito",
-        artifact = "org.mockito:mockito-core:2.28.2",
-        sha1 = "91110215a8cb9b77a46e045ee758f77d79167cc0",
-        deps = [
-            "@byte-buddy//jar",
-            "@byte-buddy-agent//jar",
-            "@objenesis//jar",
-        ],
-    )
-
-    BYTE_BUDDY_VERSION = "1.9.10"
-
-    maven_jar(
-        name = "byte-buddy",
-        artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
-        sha1 = "211a2b4d3df1eeef2a6cacf78d74a1f725e7a840",
-    )
-
-    maven_jar(
-        name = "byte-buddy-agent",
-        artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
-        sha1 = "9674aba5ee793e54b864952b001166848da0f26b",
-    )
-    maven_jar(
-        name = "objenesis",
-        artifact = "org.objenesis:objenesis:2.6",
-        sha1 = "639033469776fd37c08358c6b92a4761feb2af4b",
-    )
diff --git a/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java b/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java
index d6b8d87..a20ebed 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/quota/MaxRepositorySizeQuota.java
@@ -14,6 +14,11 @@
 
 package com.googlesource.gerrit.plugins.quota;
 
+import static com.google.gerrit.server.quota.QuotaGroupDefinitions.REPOSITORY_SIZE_GROUP;
+import static com.google.gerrit.server.quota.QuotaResponse.error;
+import static com.google.gerrit.server.quota.QuotaResponse.noOp;
+import static com.google.gerrit.server.quota.QuotaResponse.ok;
+
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.Ordering;
@@ -22,8 +27,10 @@
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.ReceivePackInitializer;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.quota.QuotaEnforcer;
+import com.google.gerrit.server.quota.QuotaRequestContext;
+import com.google.gerrit.server.quota.QuotaResponse;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Singleton;
@@ -36,7 +43,6 @@
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.time.Duration;
-import java.util.Collection;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicLong;
@@ -45,15 +51,11 @@
 import org.eclipse.jgit.internal.storage.file.GC;
 import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.PostReceiveHook;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ReceivePack;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Singleton
-public class MaxRepositorySizeQuota
-    implements ReceivePackInitializer, PostReceiveHook, RepoSizeCache {
+public class MaxRepositorySizeQuota implements QuotaEnforcer, RepoSizeCache {
   private static final Logger log = LoggerFactory.getLogger(MaxRepositorySizeQuota.class);
 
   static final String REPO_SIZE_CACHE = "repo_size";
@@ -73,26 +75,15 @@
   protected final LoadingCache<Project.NameKey, AtomicLong> cache;
   private final QuotaFinder quotaFinder;
   private final ProjectCache projectCache;
-  private final ProjectNameResolver projectNameResolver;
 
   @Inject
   protected MaxRepositorySizeQuota(
       QuotaFinder quotaFinder,
       @Named(REPO_SIZE_CACHE) LoadingCache<Project.NameKey, AtomicLong> cache,
-      ProjectCache projectCache,
-      ProjectNameResolver projectNameResolver) {
+      ProjectCache projectCache) {
     this.quotaFinder = quotaFinder;
     this.cache = cache;
     this.projectCache = projectCache;
-    this.projectNameResolver = projectNameResolver;
-  }
-
-  @Override
-  public void init(Project.NameKey project, ReceivePack rp) {
-    Optional<Long> maxPackSize = getMaxPackSize(project);
-    if (maxPackSize.isPresent()) {
-      rp.setMaxPackSizeLimit(maxPackSize.get());
-    }
   }
 
   protected Optional<Long> getMaxPackSize(Project.NameKey project) {
@@ -132,27 +123,6 @@
     }
   }
 
-  @Override
-  public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
-    Project.NameKey project = projectNameResolver.projectName(rp.getRepository());
-    if (needPack(commands)) {
-      try {
-        cache.get(project).getAndAdd(rp.getPackSize());
-      } catch (ExecutionException e) {
-        log.warn("Couldn't process onPostReceive for {}", project, e);
-      }
-    }
-  }
-
-  private boolean needPack(Collection<ReceiveCommand> commands) {
-    for (ReceiveCommand cmd : commands) {
-      if (cmd.getType() != ReceiveCommand.Type.DELETE) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   @Singleton
   static class Loader extends CacheLoader<Project.NameKey, AtomicLong> {
 
@@ -223,4 +193,75 @@
       log.warn("Error setting the size of project {}", p, e);
     }
   }
+
+  @Override
+  public QuotaResponse dryRun(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+    if (!REPOSITORY_SIZE_GROUP.equals(quotaGroup)) {
+      return noOp();
+    }
+
+    return ctx.project()
+        .flatMap(p -> getMaxPackSize(p))
+        .map(v -> requestQuota(ctx, numTokens, v, false))
+        .orElse(noOp());
+  }
+
+  @Override
+  public void refill(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+    if (!REPOSITORY_SIZE_GROUP.equals(quotaGroup)) {
+      return;
+    }
+
+    ctx.project()
+        .ifPresent(
+            p -> {
+              try {
+                cache.get(p).getAndUpdate(current -> current > numTokens ? current - numTokens : 0);
+              } catch (ExecutionException e) {
+                log.warn("Refilling [{}] bytes for repository {} failed", numTokens, p, e);
+              }
+            });
+  }
+
+  @Override
+  public QuotaResponse requestTokens(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+    if (!REPOSITORY_SIZE_GROUP.equals(quotaGroup)) {
+      return noOp();
+    }
+
+    return ctx.project()
+        .flatMap(p -> getMaxPackSize(p))
+        .map(v -> requestQuota(ctx, numTokens, v, true))
+        .orElse(noOp());
+  }
+
+  @Override
+  public QuotaResponse availableTokens(String quotaGroup, QuotaRequestContext ctx) {
+    if (!REPOSITORY_SIZE_GROUP.equals(quotaGroup)) {
+      return noOp();
+    }
+    return ctx.project().flatMap(p -> getMaxPackSize(p)).map(v -> ok(v)).orElse(noOp());
+  }
+
+  private QuotaResponse requestQuota(
+      QuotaRequestContext ctx, long requested, Long availableSpace, boolean deduct) {
+    Project.NameKey r = ctx.project().get();
+    if (availableSpace >= requested) {
+      if (deduct) {
+        try {
+          cache.get(r).getAndAdd(requested);
+        } catch (ExecutionException e) {
+          String msg = String.format("Quota request [%d] failed for repository %s", requested, r);
+          log.warn(msg, e);
+          return error(msg);
+        }
+        return ok();
+      }
+    }
+
+    return error(
+        String.format(
+            "Requested space [%d] is bigger then available [%d] for repository %s",
+            requested, availableSpace, r));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/quota/Module.java b/src/main/java/com/googlesource/gerrit/plugins/quota/Module.java
index e514334..37b7b06 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/quota/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/quota/Module.java
@@ -35,9 +35,9 @@
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
-import com.google.gerrit.server.git.ReceivePackInitializer;
 import com.google.gerrit.server.git.validators.UploadValidationListener;
 import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.quota.QuotaEnforcer;
 import com.google.gerrit.server.validators.ProjectCreationValidationListener;
 import com.google.inject.Inject;
 import com.google.inject.Provides;
@@ -50,7 +50,6 @@
 import com.googlesource.gerrit.plugins.quota.AccountLimitsConfig.Type;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.jgit.transport.PostReceiveHook;
 
 class Module extends CacheModule {
   static final String CACHE_NAME_ACCOUNTID = "rate_limits_by_account";
@@ -71,8 +70,7 @@
   protected void configure() {
     DynamicSet.bind(binder(), ProjectCreationValidationListener.class)
         .to(MaxRepositoriesQuotaValidator.class);
-    DynamicSet.bind(binder(), ReceivePackInitializer.class).to(MaxRepositorySizeQuota.class);
-    DynamicSet.bind(binder(), PostReceiveHook.class).to(MaxRepositorySizeQuota.class);
+    DynamicSet.bind(binder(), QuotaEnforcer.class).to(MaxRepositorySizeQuota.class);
     DynamicSet.bind(binder(), ProjectDeletedListener.class).to(DeletionListener.class);
     DynamicSet.bind(binder(), GarbageCollectorListener.class).to(GCListener.class);
     DynamicSet.setOf(binder(), UsageDataEventCreator.class);
@@ -89,7 +87,6 @@
         });
     bind(Publisher.class).in(Scopes.SINGLETON);
     bind(PublisherScheduler.class).in(Scopes.SINGLETON);
-    bind(ProjectNameResolver.class).in(Scopes.SINGLETON);
     bind(LifecycleListener.class)
         .annotatedWith(UniqueAnnotations.create())
         .to(PublisherScheduler.class);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/quota/ProjectNameResolver.java b/src/main/java/com/googlesource/gerrit/plugins/quota/ProjectNameResolver.java
deleted file mode 100644
index bf3ece6..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/quota/ProjectNameResolver.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2014 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.quota;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.nio.file.Path;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Repository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Singleton
-class ProjectNameResolver {
-
-  private static final Logger log = LoggerFactory.getLogger(ProjectNameResolver.class);
-  private final Path basePath;
-
-  @Inject
-  ProjectNameResolver(SitePaths site, @GerritServerConfig final Config cfg) {
-    this.basePath = site.resolve(cfg.getString("gerrit", null, "basePath"));
-  }
-
-  Project.NameKey projectName(Repository repo) {
-    Path gitDir = repo.getDirectory().toPath();
-    if (gitDir.startsWith(basePath)) {
-      String p = basePath.relativize(gitDir).toString();
-      String n = p.substring(0, p.length() - ".git".length());
-      return new Project.NameKey(n);
-    }
-    log.warn("Couldn't determine the project name from {}", gitDir);
-    return null;
-  }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/quota/DeletionListenerTest.java b/src/test/java/com/googlesource/gerrit/plugins/quota/DeletionListenerTest.java
index 034f3b2..592809e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/quota/DeletionListenerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/quota/DeletionListenerTest.java
@@ -21,16 +21,9 @@
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
 import org.junit.Test;
 
 public class DeletionListenerTest {
-
-  static {
-    KeyUtil.setEncoderImpl(new StandardKeyEncoder());
-  }
-
   protected static final String MY_PROJECT = "my-project";
 
   @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/quota/GCListenerTest.java b/src/test/java/com/googlesource/gerrit/plugins/quota/GCListenerTest.java
index 4e397ba..f73ef3d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/quota/GCListenerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/quota/GCListenerTest.java
@@ -22,16 +22,10 @@
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.events.GarbageCollectorListener;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
 import java.util.Properties;
 import org.junit.Test;
 
 public class GCListenerTest {
-  static {
-    KeyUtil.setEncoderImpl(new StandardKeyEncoder());
-  }
-
   private static final String PROJECT_NAME = "my-project";
 
   @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/quota/RepoSizeEventCreatorTest.java b/src/test/java/com/googlesource/gerrit/plugins/quota/RepoSizeEventCreatorTest.java
index 0028a04..2b40ca4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/quota/RepoSizeEventCreatorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/quota/RepoSizeEventCreatorTest.java
@@ -26,19 +26,12 @@
 import com.google.gerrit.extensions.events.UsageDataPublishedListener.Event;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
 import java.io.File;
 import java.io.IOException;
 import org.junit.Before;
 import org.junit.Test;
 
 public class RepoSizeEventCreatorTest {
-
-  static {
-    KeyUtil.setEncoderImpl(new StandardKeyEncoder());
-  }
-
   private Project.NameKey p1 = new Project.NameKey("p1");
   private Project.NameKey p2 = new Project.NameKey("p2");
   private Project.NameKey p3 = new Project.NameKey("p3");