Make uploadpack rate limit exceeded message configurable

This can be useful for some organizations to change default message to
include more info, e.g. a link to the internal rate policy.

Change-Id: Ieb450edae9948e604065f516e9337a30947daa42
diff --git a/src/main/java/com/googlesource/gerrit/plugins/quota/RateLimitUploadListener.java b/src/main/java/com/googlesource/gerrit/plugins/quota/RateLimitUploadListener.java
index 64ec2b1..8d5a9b9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/quota/RateLimitUploadListener.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/quota/RateLimitUploadListener.java
@@ -20,9 +20,11 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.cache.LoadingCache;
 import com.google.common.util.concurrent.RateLimiter;
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.git.validators.UploadValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.inject.Inject;
@@ -50,6 +52,9 @@
       LoggerFactory.getLogger(RateLimitUploadListener.class);
   private static final Method createStopwatchMethod;
   private static final Constructor<?> constructor;
+  private static final String RATE_LIMIT_TOKEN = "${rateLimit}";
+  private static final String DEFAULT_RATE_LIMIT_EXCEEDED_MSG =
+      "Exceeded rate limit of " + RATE_LIMIT_TOKEN + " fetch requests/hour";
 
   static {
     try {
@@ -118,14 +123,20 @@
   private final Provider<CurrentUser> user;
   private final LoadingCache<Account.Id, Holder> limitsPerAccount;
   private final LoadingCache<String, Holder> limitsPerRemoteHost;
+  private final String limitExceededMsg;
 
   @Inject
   RateLimitUploadListener(Provider<CurrentUser> user,
       @Named(CACHE_NAME_ACCOUNTID) LoadingCache<Account.Id, Holder> limitsPerAccount,
-      @Named(CACHE_NAME_REMOTEHOST) LoadingCache<String, Holder> limitsPerRemoteHost) {
+      @Named(CACHE_NAME_REMOTEHOST) LoadingCache<String, Holder> limitsPerRemoteHost,
+      PluginConfigFactory cfg,
+      @PluginName String pluginName) {
     this.user = user;
     this.limitsPerAccount = limitsPerAccount;
     this.limitsPerRemoteHost = limitsPerRemoteHost;
+    String msg = cfg.getFromGerritConfig(pluginName).getString(
+        "uploadpackLimitExceededMsg", DEFAULT_RATE_LIMIT_EXCEEDED_MSG);
+    limitExceededMsg = msg.replace(RATE_LIMIT_TOKEN, "{0,number,##.##}");
   }
 
   @Override
@@ -155,7 +166,7 @@
     }
     if (limiter != null && !limiter.tryAcquire()) {
       throw new RateLimitException(
-          MessageFormat.format("Exceeded rate limit of {0,number,##.##} fetch requests/hour",
+          MessageFormat.format(limitExceededMsg,
               limiter.getRate() * SECONDS_PER_HOUR));
     }
   }
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 70d98cb..61e4a77 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -217,6 +217,12 @@
   [group "Registered Users"]
     uploadpack = 30/hour burst 60
 ```
+The rate limit exceeded message can be configured by setting parameter
+`uploadpackLimitExceededMsg` in the `plugin.quota` subsection of the
+`gerrit.config` file. `${rateLimit}` token is supported in the message and
+will be replaced by effective rate limit per hour.
+
+Defaults to `Exceeded rate limit of ${rateLimit} fetch requests/hour`
 
 Publication Schedule
 --------------------