Add parameters to plugin configuration

Add http-transform-pattern and storage-directory parameters to plugin
configuration in gerrit.config.

Parameter value is resolved in the following context:
* storage-directory
1. if /tmp/javamelody directory exists it will be kept
2. plugin.javamelody.storage-directory has higher priority than
-Djavamelody.storage-directory value configured for
container.javaOptions parameter
3. finally it resolves to default which is GERRIT_SITE/data/javamelody
directory

* http-transform-pattern
1. plugin.javamelody.http-transform-pattern has higher priority than
-Djavamelody.http-transform-pattern value configured for
container.javaOptions parameter
2. finally it resolves to default that covers REST and GIT HTTP
(including LFS) requests

Change-Id: I18435a792c9b0c43fef86da954358e2137d0ce77
Signed-off-by: Jacek Centkowski <jcentkowski@collab.net>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java b/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java
index 60788a3..6e748fe 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java
@@ -14,14 +14,19 @@
 
 package com.googlesource.gerrit.plugins.javamelody;
 
+import com.google.common.base.Strings;
 import com.google.gerrit.extensions.annotations.PluginData;
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.httpd.AllRequestFilter;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Optional;
 import java.util.StringJoiner;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -36,6 +41,7 @@
 
 @Singleton
 class GerritMonitoringFilter extends AllRequestFilter {
+  private static final Logger log = LoggerFactory.getLogger(GerritMonitoringFilter.class);
   private final JavamelodyFilter monitoring;
   private final CapabilityChecker capabilityChecker;
 
@@ -81,7 +87,6 @@
   }
 
   static class JavamelodyFilter extends MonitoringFilter {
-    private static final Logger log = LoggerFactory.getLogger(JavamelodyFilter.class);
     private static final String JAVAMELODY_PREFIX = "javamelody";
     private static final String HTTP_TRANSFORM_PATTERN = "http-transform-pattern";
     private static final String GLOBAL_HTTP_TRANSFORM_PATTERN =
@@ -104,20 +109,27 @@
             .add("\\d+") // various ids e.g. change id
             .toString();
 
+    private final PluginConfig cfg;
     private final Path defaultDataDir;
 
     @Inject
-    JavamelodyFilter(@PluginData Path defaultDataDir) {
+    JavamelodyFilter(
+        PluginConfigFactory cfgFactory,
+        @PluginName String pluginName,
+        @PluginData Path defaultDataDir) {
       this.defaultDataDir = defaultDataDir;
+      this.cfg = cfgFactory.getFromGerritConfig(pluginName);
     }
 
     @Override
     public void init(FilterConfig config) throws ServletException {
-      if (isPropertyUndefined(config, HTTP_TRANSFORM_PATTERN, GLOBAL_HTTP_TRANSFORM_PATTERN)) {
-        System.setProperty(GLOBAL_HTTP_TRANSFORM_PATTERN, GERRIT_GROUPING);
+      if (isPropertyInPluginConfig(HTTP_TRANSFORM_PATTERN)
+          || isPropertyUndefined(config, HTTP_TRANSFORM_PATTERN, GLOBAL_HTTP_TRANSFORM_PATTERN)) {
+        System.setProperty(GLOBAL_HTTP_TRANSFORM_PATTERN, getTransformPattern());
       }
 
-      if (isPropertyUndefined(config, STORAGE_DIR, GLOBAL_STORAGE_DIR)) {
+      if (isPropertyInPluginConfig(STORAGE_DIR)
+          || isPropertyUndefined(config, STORAGE_DIR, GLOBAL_STORAGE_DIR)) {
         System.setProperty(GLOBAL_STORAGE_DIR, getStorageDir());
       }
 
@@ -128,23 +140,34 @@
       return getMonitoringUrl(httpRequest);
     }
 
+    private String getTransformPattern() {
+      return cfg.getString(HTTP_TRANSFORM_PATTERN, GERRIT_GROUPING);
+    }
+
     private String getStorageDir() {
       // default to old path for javamelody storage-directory if it exists
       final Path tmp = Paths.get(System.getProperty("java.io.tmpdir")).resolve(JAVAMELODY_PREFIX);
       if (Files.isDirectory(tmp)) {
+        log.warn("Javamelody data exists in 'tmp' [{}]. Configuration (if any) will be ignored.", tmp);
         return tmp.toString();
       }
 
-      // put javamelody data in default plugin data dir
-      if (!Files.isDirectory(defaultDataDir)) {
+      // plugin config has the highest priority
+      Path storageDir =
+          Optional.ofNullable(cfg.getString(STORAGE_DIR)).map(Paths::get).orElse(defaultDataDir);
+      if (!Files.isDirectory(storageDir)) {
         try {
-          Files.createDirectories(defaultDataDir);
+          Files.createDirectories(storageDir);
         } catch (IOException e) {
-          log.error("Creation of javamelody data dir [{}] failed.", defaultDataDir, e);
+          log.error("Creation of javamelody data dir [{}] failed.", storageDir, e);
           throw new RuntimeException(e);
         }
       }
-      return defaultDataDir.toString();
+      return storageDir.toString();
+    }
+
+    private boolean isPropertyInPluginConfig(String name) {
+      return !Strings.isNullOrEmpty(cfg.getString(name));
     }
 
     private boolean isPropertyUndefined(FilterConfig config, String name, String globalName) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index b42bc36..dd74e42 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -20,6 +20,33 @@
 
 <a id="allowTopMenu">
 `plugin.@PLUGIN@.allowTopMenu`
-:	Whether it is allowed to show top menu in Gerrit UI.
-	By default true.
+: Whether it is allowed to show top menu in Gerrit UI.
+  By default true.
+
+<a id="storage-directory">
+`plugin.@PLUGIN@.storage-directory`
+: The directory in which to store data files. Javamelody, by default,
+  stores data under `/tmp/javamelody` directory but it gets wiped out
+  upon system restart. Therefore for fresh install (or when it was just
+  wiped out after restart) it is defaulted to `GERRIT_SITE/data/@PLUGIN@`.
+  Note that, in order to preserve existing configuration through
+  `-Djavamelody.storage-directory` value from `container.javaOptions`,
+  it has lower priority than `plugin.@PLUGIN@.storage-directory` but higher
+  than default.
+
+<a id="http-transform-pattern">
+`plugin.@PLUGIN@.http-transform-pattern`
+: Grouping pattern for HTTP requests statistics. Without groupping pattern
+  javamelody treats each HTTP requests as distinctive therefore it is not
+  possible to deduct overal site performance and what is more, on busy server,
+  it may lead to
+  [issue with too many open RRD files](https://stackoverflow.com/questions/19147762/javamelody-crashing-the-server-with-thousands-of-rrd-files).
+  If not specified this parameter takes the value that allows javamelody to
+  group all REST and GIT HTTP (incuding LFS) requests over project, account,
+  SHA-1, Long Object Id (LFS), account etc. ids. However one can provide own
+  regexp to cover for instance plugin extensions.
+  Note that, in order to preserve existing configuration through
+  `-Djavamelody.http-transform-pattern` value from `container.javaOptions`,
+  it has lower priority than `plugin.@PLUGIN@.http-transform-pattern` but higher
+  than default.