CommentLinkProvider: Support reloading configurations

Change-Id: Ibac61e1cac2974a8c07d0fbb5fab4b6332a8a0d2
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 0e769b5..49d9f49 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1334,6 +1334,11 @@
 configuration 'tracker' uses raw HTML to more precisely control
 how the replacement is displayed to the user.
 
+commentlinks supports configuration reloads:
+link:cmd-reload-config.html[reload-config]. Though a
+link:cmd-flush-caches.html[flush-caches] of "projects" is needed for the
+commentlinks to be immediately available in the UI.
+
 ----
 [commentlink "changeid"]
   match = (I[0-9a-f]{8,40})
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 8b3dbfb..5da5d1a 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -427,9 +427,8 @@
 
     bind(AccountManager.class);
 
-    bind(new TypeLiteral<List<CommentLinkInfo>>() {})
-        .toProvider(CommentLinkProvider.class)
-        .in(SINGLETON);
+    bind(new TypeLiteral<List<CommentLinkInfo>>() {}).toProvider(CommentLinkProvider.class);
+    DynamicSet.bind(binder(), GerritConfigListener.class).to(CommentLinkProvider.class);
 
     bind(ReloadPluginListener.class)
         .annotatedWith(UniqueAnnotations.create())
diff --git a/java/com/google/gerrit/server/project/CommentLinkProvider.java b/java/com/google/gerrit/server/project/CommentLinkProvider.java
index bc70232..516965b 100644
--- a/java/com/google/gerrit/server/project/CommentLinkProvider.java
+++ b/java/com/google/gerrit/server/project/CommentLinkProvider.java
@@ -17,27 +17,31 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
+import com.google.gerrit.server.config.ConfigUpdatedEvent;
+import com.google.gerrit.server.config.GerritConfigListener;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class CommentLinkProvider implements Provider<List<CommentLinkInfo>> {
+@Singleton
+public class CommentLinkProvider implements Provider<List<CommentLinkInfo>>, GerritConfigListener {
   private static final Logger log = LoggerFactory.getLogger(CommentLinkProvider.class);
 
-  private final Config cfg;
+  private volatile List<CommentLinkInfo> commentLinks;
 
   @Inject
   CommentLinkProvider(@GerritServerConfig Config cfg) {
-    this.cfg = cfg;
+    this.commentLinks = parseConfig(cfg);
   }
 
-  @Override
-  public List<CommentLinkInfo> get() {
+  private List<CommentLinkInfo> parseConfig(Config cfg) {
     Set<String> subsections = cfg.getSubsections(ProjectConfig.COMMENTLINK);
     List<CommentLinkInfo> cls = Lists.newArrayListWithCapacity(subsections.size());
     for (String name : subsections) {
@@ -54,4 +58,18 @@
     }
     return ImmutableList.copyOf(cls);
   }
+
+  @Override
+  public List<CommentLinkInfo> get() {
+    return commentLinks;
+  }
+
+  @Override
+  public List<ConfigUpdatedEvent.Update> configUpdated(ConfigUpdatedEvent event) {
+    if (event.isSectionUpdated(ProjectConfig.COMMENTLINK)) {
+      commentLinks = parseConfig(event.getNewConfig());
+      return Collections.singletonList(event.accept(ProjectConfig.COMMENTLINK));
+    }
+    return Collections.emptyList();
+  }
 }