From b27c9397c882ca2175ec7221cc54eca8351d1239 Mon Sep 17 00:00:00 2001 From: Edwin Kempin Date: Tue, 19 Nov 2013 13:12:43 +0100 Subject: [PATCH] Add an event for the update of a project's HEAD When the HEAD of a project is updated Gerrit now informs about this by sending an event. Plugins can implement a listener to be notified about this event. Having this new event enables the replication plugin to replicate HEAD updates. Change-Id: I4dd7969020880190668190e8345197a23dbc92f1 Signed-off-by: Edwin Kempin --- Documentation/dev-plugins.txt | 4 ++ .../events/HeadUpdatedListener.java | 29 ++++++++++++ .../server/config/GerritGlobalModule.java | 2 + .../google/gerrit/server/project/SetHead.java | 44 +++++++++++++++++-- 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index d1c79ac82d..95f960fcc4 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -364,6 +364,10 @@ Project creation + Project deletion +* `com.google.gerrit.extensions.events.HeadUpdatedListener`: ++ +Update of HEAD on a project + [[stream-events]] Sending Events to the Events Stream ----------------------------------- diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java new file mode 100644 index 0000000000..5961d6ffa5 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java @@ -0,0 +1,29 @@ +// Copyright (C) 2013 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.google.gerrit.extensions.events; + +import com.google.gerrit.extensions.annotations.ExtensionPoint; + +/** Notified whenever the HEAD of a project is updated. */ +@ExtensionPoint +public interface HeadUpdatedListener { + public interface Event { + String getProjectName(); + String getOldHeadName(); + String getNewHeadName(); + } + + void onHeadUpdated(Event event); +} 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 87f5271567..4b0600a2d6 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 @@ -23,6 +23,7 @@ import com.google.gerrit.extensions.config.CapabilityDefinition; import com.google.gerrit.extensions.config.DownloadCommand; import com.google.gerrit.extensions.config.DownloadScheme; import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; +import com.google.gerrit.extensions.events.HeadUpdatedListener; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.events.ProjectDeletedListener; @@ -241,6 +242,7 @@ public class GerritGlobalModule extends FactoryModule { DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class); DynamicSet.setOf(binder(), NewProjectCreatedListener.class); DynamicSet.setOf(binder(), ProjectDeletedListener.class); + DynamicSet.setOf(binder(), HeadUpdatedListener.class); DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ChangeCache.class); DynamicSet.setOf(binder(), ChangeListener.class); DynamicSet.setOf(binder(), CommitValidationListener.class); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java index 2f8e26d767..05b392b536 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java @@ -15,6 +15,8 @@ package com.google.gerrit.server.project; import com.google.common.base.Strings; +import com.google.gerrit.extensions.events.HeadUpdatedListener; +import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.DefaultInput; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; @@ -31,10 +33,14 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; public class SetHead implements RestModifyView { + private static final Logger log = LoggerFactory.getLogger(SetHead.class); + static class Input { @DefaultInput String ref; @@ -42,15 +48,19 @@ public class SetHead implements RestModifyView { private final GitRepositoryManager repoManager; private final Provider identifiedUser; + private final DynamicSet headUpdatedListener; @Inject - SetHead(GitRepositoryManager repoManager, Provider identifiedUser) { + SetHead(GitRepositoryManager repoManager, + Provider identifiedUser, + DynamicSet headUpdatedListener) { this.repoManager = repoManager; this.identifiedUser = identifiedUser; + this.headUpdatedListener = headUpdatedListener; } @Override - public String apply(ProjectResource rsrc, Input input) throws AuthException, + public String apply(final ProjectResource rsrc, Input input) throws AuthException, ResourceNotFoundException, BadRequestException, UnprocessableEntityException, IOException { if (!rsrc.getControl().isOwner()) { @@ -72,10 +82,12 @@ public class SetHead implements RestModifyView { "Ref Not Found: %s", ref)); } - if (!repo.getRef(Constants.HEAD).getTarget().getName().equals(ref)) { + final String oldHead = repo.getRef(Constants.HEAD).getTarget().getName(); + final String newHead = ref; + if (!oldHead.equals(newHead)) { final RefUpdate u = repo.updateRef(Constants.HEAD, true); u.setRefLogIdent(identifiedUser.get().newRefLogIdent()); - RefUpdate.Result res = u.link(ref); + RefUpdate.Result res = u.link(newHead); switch(res) { case NO_CHANGE: case RENAMED: @@ -85,6 +97,30 @@ public class SetHead implements RestModifyView { default: throw new IOException("Setting HEAD failed with " + res); } + + HeadUpdatedListener.Event event = new HeadUpdatedListener.Event() { + @Override + public String getProjectName() { + return rsrc.getNameKey().get(); + } + + @Override + public String getOldHeadName() { + return oldHead; + } + + @Override + public String getNewHeadName() { + return newHead; + } + }; + for (HeadUpdatedListener l : headUpdatedListener) { + try { + l.onHeadUpdated(event); + } catch (RuntimeException e) { + log.warn("Failure in HeadUpdatedListener", e); + } + } } return ref; } catch (RepositoryNotFoundException e) {