Migrate configuration to <plugin-name>.config from gerrit.config

Move existing configuration to <plugin-name>.config as it provides
more flexibility when naming i.e subsection names can be used and
makes them readable. Using a <plugin-name>.config also has the
added advantage of having all plugin related configs in a single
location.

For backward compatibility, existing configuration is still read from
gerrit config if the plugin config is not found.

Change-Id: If0ed517406c3f5a78c8b0328ef5d4c23c8326dae
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java b/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java
index 268b1a3..c687d72 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java
@@ -14,7 +14,6 @@
 
 package com.googlesource.gerrit.plugins.task;
 
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.query.AndPredicate;
 import com.google.gerrit.index.query.NotPredicate;
@@ -22,7 +21,6 @@
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeIndexPredicate;
@@ -34,10 +32,6 @@
 import com.googlesource.gerrit.plugins.task.statistics.HitHashMap;
 import com.googlesource.gerrit.plugins.task.statistics.StopWatch;
 import com.googlesource.gerrit.plugins.task.util.ThrowingProvider;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import org.eclipse.jgit.lib.Config;
 
 public class PredicateCache {
   public static class Statistics {
@@ -45,7 +39,7 @@
   }
 
   protected final SubmitRequirementChangeQueryBuilder srcqb;
-  protected final Set<String> cacheableByBranchPredicateClassNames;
+  protected final TaskPluginConfiguration config;
   protected final CurrentUser user;
   protected final HitHashMap<String, ThrowingProvider<Predicate<ChangeData>, QueryParseException>>
       predicatesByQuery = new HitHashMap<>();
@@ -54,16 +48,10 @@
 
   @Inject
   public PredicateCache(
-      @GerritServerConfig Config config,
-      @PluginName String pluginName,
-      CurrentUser user,
-      SubmitRequirementChangeQueryBuilder srcqb) {
+      TaskPluginConfiguration config, CurrentUser user, SubmitRequirementChangeQueryBuilder srcqb) {
+    this.config = config;
     this.user = user;
     this.srcqb = srcqb;
-    cacheableByBranchPredicateClassNames =
-        new HashSet<>(
-            Arrays.asList(
-                config.getStringList(pluginName, "cacheable-predicates", "byBranch-className")));
   }
 
   public void initStatistics(int summaryCount) {
@@ -133,6 +121,8 @@
         return true;
       }
     }
-    return cacheableByBranchPredicateClassNames.contains(predicate.getClass().getName());
+    return config
+        .getCacheableByBranchPredicateClassNames()
+        .contains(predicate.getClass().getName());
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java
new file mode 100644
index 0000000..888c529
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2024 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.task;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import org.eclipse.jgit.lib.Config;
+
+@Singleton
+public class TaskPluginConfiguration {
+  private static final String CACHEABLE_PREDICATES = "cacheablePredicates";
+  private static final String CACHEABLE_PREDICATES_SECTION = "byBranch";
+  private static final String CACHEABLE_PREDICATES_KEY = "className";
+  private static final String DEPRECATED_CACHEABLE_PREDICATES = "cacheable-predicates";
+  private static final String DEPRECATED_CACHEABLE_PREDICATES_KEY = "byBranch-className";
+
+  private final Set<String> cacheableByBranchPredicateClassNames;
+  private final Config gerritConfig;
+  private final Config pluginConfig;
+  private final String plugin;
+
+  @Inject
+  public TaskPluginConfiguration(
+      @PluginName String plugin,
+      @GerritServerConfig Config gerritConfig,
+      PluginConfigFactory pluginConfigFactory) {
+    this.plugin = plugin;
+    this.gerritConfig = gerritConfig;
+    this.pluginConfig = pluginConfigFactory.getGlobalPluginConfig(plugin);
+    cacheableByBranchPredicateClassNames =
+        new HashSet<>(Arrays.asList(readCacheableByBranchPredicateClassNames()));
+  }
+
+  public Set<String> getCacheableByBranchPredicateClassNames() {
+    return cacheableByBranchPredicateClassNames;
+  }
+
+  private String[] readCacheableByBranchPredicateClassNames() {
+    String[] fromPluginConfig =
+        pluginConfig.getStringList(
+            CACHEABLE_PREDICATES, CACHEABLE_PREDICATES_SECTION, CACHEABLE_PREDICATES_KEY);
+    if (fromPluginConfig.length > 0) {
+      return fromPluginConfig;
+    }
+    // Read from gerrit config for backward compatibility. This can be removed once all known users
+    // have migrated to plugin config.
+    return gerritConfig.getStringList(
+        plugin, DEPRECATED_CACHEABLE_PREDICATES, DEPRECATED_CACHEABLE_PREDICATES_KEY);
+  }
+}
diff --git a/src/main/resources/Documentation/config-gerrit.md b/src/main/resources/Documentation/config-gerrit.md
deleted file mode 100644
index 6f0b0a5..0000000
--- a/src/main/resources/Documentation/config-gerrit.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Admin User Guide - Configuration
-
-## File `etc/gerrit.config`
-
-The file `'$site_path'/etc/gerrit.config` is a Git-style config file
-that controls many host specific settings for Gerrit.
-
-### Section @PLUGIN@ "cacheable-predicates"
-
-The @PLUGIN@.cacheable-predicates section configures Change Predicate
-optimizations which the @PLUGIN@ plugin may use when evaluating tasks.
-
-#### @PLUGIN@.cacheable-predicates.byBranch-className
-
-The value set with this key specifies a fully qualified class name
-of a Predicate which can be assumed to always return the same match
-result to all Changes destined for the same project/branch
-combinations. This key may be specified more than once.
-
-Example:
-
-```
-[@PLUGIN@ "cacheable-predicates"]
-        byBranch-className = com.google.gerrit.server.query.change.BranchSetPredicate
-```
\ No newline at end of file
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
new file mode 100644
index 0000000..7900ef7
--- /dev/null
+++ b/src/main/resources/Documentation/config.md
@@ -0,0 +1,25 @@
+# Admin User Guide - Configuration
+
+## File `etc/@PLUGIN@.config`
+
+The file `'$site_path'/etc/@PLUGIN@.config` is a Git-style config file
+that controls settings for @PLUGIN@ plugin.
+
+### Section "cacheablePredicates"
+
+The cacheablePredicates section configures Change Predicate
+optimizations which the @PLUGIN@ plugin may use when evaluating tasks.
+
+#### cacheablePredicates.byBranch.className
+
+The value set with this key specifies a fully qualified class name
+of a Predicate which can be assumed to always return the same match
+result to all Changes destined for the same project/branch
+combinations. This key may be specified more than once.
+
+Example:
+
+```
+[cacheablePredicates "byBranch"]
+        className = com.google.gerrit.server.query.change.BranchSetPredicate
+```