Enable plugins to listen on updates of plugin project config
Change-Id: I6dbb45ed79385ae7299fb83992072dab57485332
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 2b07cf7..c27811a 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -710,6 +710,10 @@
}
----
+By overwriting the `onUpdate` method of `ProjectConfigEntry` plugins
+can be notified when this configuration parameter is updated on a
+project.
+
[[project-specific-configuration]]
== Project Specific Configuration in own config file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 290a223..c9eabe8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -260,6 +260,8 @@
DynamicSet.setOf(binder(), HeadUpdatedListener.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ChangeCache.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(MergeabilityChecker.class);
+ DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
+ .to(ProjectConfigEntry.UpdateChecker.class);
DynamicSet.setOf(binder(), ChangeListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), MergeValidationListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java
index c7cd48e..c8df7c0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectConfigEntry.java
@@ -17,8 +17,23 @@
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.DynamicMap.Entry;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -126,4 +141,103 @@
public boolean isEditable(ProjectState project) {
return true;
}
+
+ public void onUpdate(Project.NameKey project, String oldValue, String newValue) {
+ }
+
+ public void onUpdate(Project.NameKey project, Boolean oldValue, Boolean newValue) {
+ }
+
+ public void onUpdate(Project.NameKey project, Integer oldValue, Integer newValue) {
+ }
+
+ public void onUpdate(Project.NameKey project, Long oldValue, Long newValue) {
+ }
+
+ public static class UpdateChecker implements GitReferenceUpdatedListener {
+ private static final Logger log = LoggerFactory.getLogger(UpdateChecker.class);
+
+ private final MetaDataUpdate.Server metaDataUpdateFactory;
+ private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
+
+ @Inject
+ UpdateChecker(MetaDataUpdate.Server metaDataUpdateFactory,
+ DynamicMap<ProjectConfigEntry> pluginConfigEntries) {
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.pluginConfigEntries = pluginConfigEntries;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ Project.NameKey p = new Project.NameKey(event.getProjectName());
+ if (!event.getRefName().equals(RefNames.REFS_CONFIG)) {
+ return;
+ }
+
+ try {
+ ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId());
+ ProjectConfig newCfg = parseConfig(p, event.getNewObjectId());
+ if (oldCfg != null && newCfg != null) {
+ for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
+ ProjectConfigEntry configEntry = e.getProvider().get();
+ String newValue = getValue(newCfg, e);
+ String oldValue = getValue(oldCfg, e);
+ if ((newValue == null && oldValue == null)
+ || (newValue != null && newValue.equals(oldValue))) {
+ return;
+ }
+
+ switch (configEntry.getType()) {
+ case BOOLEAN:
+ configEntry.onUpdate(p, toBoolean(oldValue), toBoolean(newValue));
+ break;
+ case INT:
+ configEntry.onUpdate(p, toInt(oldValue), toInt(newValue));
+ break;
+ case LONG:
+ configEntry.onUpdate(p, toLong(oldValue), toLong(newValue));
+ break;
+ case LIST:
+ case STRING:
+ default:
+ configEntry.onUpdate(p, oldValue, newValue);
+ }
+ }
+ }
+ } catch (IOException | ConfigInvalidException e) {
+ log.error(String.format(
+ "Failed to check if plugin config of project %s was updated.",
+ p.get()), e);
+ }
+ }
+
+ private ProjectConfig parseConfig(Project.NameKey p, String idStr)
+ throws IOException, ConfigInvalidException, RepositoryNotFoundException {
+ ObjectId id = ObjectId.fromString(idStr);
+ if (ObjectId.zeroId().equals(id)) {
+ return null;
+ }
+ return ProjectConfig.read(metaDataUpdateFactory.create(p), id);
+ }
+
+ private static String getValue(ProjectConfig cfg, Entry<ProjectConfigEntry> e) {
+ String value = cfg.getPluginConfig(e.getPluginName()).getString(e.getExportName());
+ if (value == null) {
+ value = e.getProvider().get().getDefaultValue();
+ }
+ return value;
+ }
+ }
+
+ private static Boolean toBoolean(String value) {
+ return value != null ? Boolean.parseBoolean(value) : null;
+ }
+
+ private static int toInt(String value) {
+ return value != null ? Integer.parseInt(value) : null;
+ }
+
+ private static long toLong(String value) {
+ return value != null ? Long.parseLong(value) : null;
+ }
}