Merge "Test Change-Ids checks on push with CreateNewChangeForAllNotInTarget" into stable-2.13
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index d085731..9752d14 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2220,7 +2220,17 @@
 Note that metrics cannot be recorded from plugin init steps that
 are run during site initialization.
 
-Plugin metrics are recorded under `plugins/${plugin-name}/${metric-name}`.
+By default, plugin metrics are recorded under
+`plugins/${plugin-name}/${metric-name}`. This can be changed by
+setting `plugins.${plugin-name}.metricsPrefix` in the `gerrit.config`
+file. For example:
+
+----
+  [plugin "my-plugin"]
+    metricsPrefix = my-metrics
+----
+
+will cause the metrics to be recorded under `my-metrics/${metric-name}`.
 
 See the replication metrics in the
 link:https://gerrit.googlesource.com/plugins/replication/+/master/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationMetrics.java[
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
index e159c82..6359bb5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
@@ -337,10 +337,15 @@
 
   private synchronized void define(String name, Description desc) {
     if (descriptions.containsKey(name)) {
-      throw new IllegalStateException(String.format(
-          "metric %s already defined", name));
+      ImmutableMap<String, String> annotations = descriptions.get(name);
+      if (!desc.getAnnotations().get(Description.DESCRIPTION).equals(
+          annotations.get(Description.DESCRIPTION))) {
+        throw new IllegalStateException(String.format(
+            "metric %s already defined", name));
+      }
+    } else {
+      descriptions.put(name, desc.getAnnotations());
     }
-    descriptions.put(name, desc.getAnnotations());
   }
 
   private static final Pattern METRIC_NAME_PATTERN = Pattern
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
index 926ef44..fa913b2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -17,6 +17,8 @@
 import static com.google.gerrit.server.plugins.PluginLoader.asTemp;
 
 import com.google.common.base.MoreObjects;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Inject;
 
@@ -45,10 +47,13 @@
   static final Logger log = LoggerFactory.getLogger(JarPluginProvider.class);
 
   private final Path tmpDir;
+  private final PluginConfigFactory configFactory;
 
   @Inject
-  JarPluginProvider(SitePaths sitePaths) {
-    tmpDir = sitePaths.tmp_dir;
+  JarPluginProvider(SitePaths sitePaths,
+      PluginConfigFactory configFactory) {
+    this.tmpDir = sitePaths.tmp_dir;
+    this.configFactory = configFactory;
   }
 
   @Override
@@ -143,9 +148,12 @@
               PluginLoader.parentFor(type));
 
       JarScanner jarScanner = createJarScanner(tmp);
+      PluginConfig pluginConfig = configFactory.getFromGerritConfig(name);
+
       ServerPlugin plugin = new ServerPlugin(name, description.canonicalUrl,
           description.user, srcJar, snapshot, jarScanner,
-          description.dataDir, pluginLoader);
+          description.dataDir, pluginLoader,
+          pluginConfig.getString("metricsPrefix", null));
       plugin.setCleanupHandle(new CleanupHandle(tmp, jarFile));
       keep = true;
       return plugin;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginMetricMaker.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginMetricMaker.java
index 724ebeb..23b1eee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginMetricMaker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginMetricMaker.java
@@ -45,9 +45,9 @@
   private final String prefix;
   private final Set<RegistrationHandle> cleanup;
 
-  public PluginMetricMaker(MetricMaker root, String pluginName) {
+  public PluginMetricMaker(MetricMaker root, String prefix) {
     this.root = root;
-    this.prefix = String.format("plugins/%s/", pluginName);
+    this.prefix = prefix.endsWith("/") ? prefix : prefix + "/";
     cleanup = Collections.synchronizedSet(new HashSet<RegistrationHandle>());
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
index 59ed261..40f1fea 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
@@ -42,6 +42,7 @@
   private final Path dataDir;
   private final String pluginCanonicalWebUrl;
   private final ClassLoader classLoader;
+  private final String metricsPrefix;
   private Class<? extends Module> sysModule;
   private Class<? extends Module> sshModule;
   private Class<? extends Module> httpModule;
@@ -59,7 +60,8 @@
       FileSnapshot snapshot,
       PluginContentScanner scanner,
       Path dataDir,
-      ClassLoader classLoader) throws InvalidPluginException {
+      ClassLoader classLoader,
+      String metricsPrefix) throws InvalidPluginException {
     super(name, srcJar, pluginUser, snapshot,
         Plugin.getApiType(getPluginManifest(scanner)));
     this.pluginCanonicalWebUrl = pluginCanonicalWebUrl;
@@ -67,9 +69,22 @@
     this.dataDir = dataDir;
     this.classLoader = classLoader;
     this.manifest = getPluginManifest(scanner);
+    this.metricsPrefix = metricsPrefix;
     loadGuiceModules(manifest, classLoader);
   }
 
+  public ServerPlugin(String name,
+      String pluginCanonicalWebUrl,
+      PluginUser pluginUser,
+      Path srcJar,
+      FileSnapshot snapshot,
+      PluginContentScanner scanner,
+      Path dataDir,
+      ClassLoader classLoader) throws InvalidPluginException {
+    this(name, pluginCanonicalWebUrl, pluginUser, srcJar, snapshot, scanner,
+        dataDir, classLoader, null);
+  }
+
   private void loadGuiceModules(Manifest manifest, ClassLoader classLoader) throws InvalidPluginException {
     Attributes main = manifest.getMainAttributes();
     String sysName = main.getValue("Gerrit-Module");
@@ -116,6 +131,10 @@
     return pluginCanonicalWebUrl;
   }
 
+  String getMetricsPrefix() {
+    return metricsPrefix;
+  }
+
   private static Manifest getPluginManifest(PluginContentScanner scanner)
       throws InvalidPluginException {
     try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
index a7f0087..ff89cef4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.plugins;
 
+import com.google.common.base.MoreObjects;
 import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl;
 import com.google.gerrit.extensions.annotations.PluginData;
 import com.google.gerrit.extensions.annotations.PluginName;
@@ -57,7 +58,8 @@
       public void configure() {
         PluginMetricMaker metrics = new PluginMetricMaker(
             serverMetrics,
-            plugin.getName());
+            MoreObjects.firstNonNull(plugin.getMetricsPrefix(),
+                String.format("plugins/%s/", plugin.getName())));
         bind(MetricMaker.class).toInstance(metrics);
         listener().toInstance(metrics);
       }