Set logging tag with plugin name on invocation of extension point

With this logging tag one can easily see in the log file which logs have
been triggered by the plugin. For traces this is interesting because
then for actions such as loading files, doing index queries etc. one can
see if they have been triggered by a plugin. E.g. if there is an
extensive number of index queries that are triggered by a plugin we want
to see this in the trace.

Such logging tags are mostly useful for extension points that are likely
to have complex or expensive implementations. We may not want to set
such a logging tag for extension points which only have trivial
implementations.

Setting the logging tag with the plugin name when an extension point is
invoked is done by opening a trace context. To invoke extension points
with such a trace context they should be invoked through a plugin
context. The plugin context can be directly injected. Instead of
injecting DynamicItem/DynamicSet/DynamicMap you can now inject
PluginItemContext/PluginSetContext/PluginMapContext.

The plugin context classes offer methods to invoke the extension
points that automatically set the trace context. In addition they
provide functionality for catching and logging exceptions from plugins.
This makes the logging and exception handling for plugin invocations
more consistent across the code base and removes the need to have code
for this in multiple places.

Since exception handling with plugin contexts is easy now more plugin
invocations handle exceptions now which makes Gerrit more resilient
against plugin failures.

This change adapts calls to most important extensions points, such as
validators and listeners.

Change-Id: Iab41d0059049f06ca41b697e93aa6a1f9668de5b
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
Edwin Kempin
2018-08-24 15:49:36 +02:00
parent 92225eb622
commit 5eb0219eb3
57 changed files with 1589 additions and 594 deletions

View File

@@ -19,6 +19,7 @@ import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.account.UniversalGroupBackend; import com.google.gerrit.server.account.UniversalGroupBackend;
import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -34,18 +35,16 @@ public class StartupChecks implements LifecycleListener {
} }
} }
private final DynamicSet<StartupCheck> startupChecks; private final PluginSetContext<StartupCheck> startupChecks;
@Inject @Inject
StartupChecks(DynamicSet<StartupCheck> startupChecks) { StartupChecks(PluginSetContext<StartupCheck> startupChecks) {
this.startupChecks = startupChecks; this.startupChecks = startupChecks;
} }
@Override @Override
public void start() throws StartupException { public void start() throws StartupException {
for (StartupCheck startupCheck : startupChecks) { startupChecks.runEach(c -> c.check(), StartupException.class);
startupCheck.check();
}
} }
@Override @Override

View File

@@ -14,13 +14,13 @@
package com.google.gerrit.server.account; package com.google.gerrit.server.account;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ServerInitiated; import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.AccountActivationValidationListener; import com.google.gerrit.server.validators.AccountActivationValidationListener;
import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.server.validators.ValidationException;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
@@ -35,13 +35,13 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton @Singleton
public class SetInactiveFlag { public class SetInactiveFlag {
private final DynamicSet<AccountActivationValidationListener> private final PluginSetContext<AccountActivationValidationListener>
accountActivationValidationListeners; accountActivationValidationListeners;
private final Provider<AccountsUpdate> accountsUpdateProvider; private final Provider<AccountsUpdate> accountsUpdateProvider;
@Inject @Inject
SetInactiveFlag( SetInactiveFlag(
DynamicSet<AccountActivationValidationListener> accountActivationValidationListeners, PluginSetContext<AccountActivationValidationListener> accountActivationValidationListeners,
@ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) { @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
this.accountActivationValidationListeners = accountActivationValidationListeners; this.accountActivationValidationListeners = accountActivationValidationListeners;
this.accountsUpdateProvider = accountsUpdateProvider; this.accountsUpdateProvider = accountsUpdateProvider;
@@ -60,13 +60,12 @@ public class SetInactiveFlag {
if (!a.getAccount().isActive()) { if (!a.getAccount().isActive()) {
alreadyInactive.set(true); alreadyInactive.set(true);
} else { } else {
for (AccountActivationValidationListener l : accountActivationValidationListeners) { try {
try { accountActivationValidationListeners.runEach(
l.validateDeactivation(a); l -> l.validateDeactivation(a), ValidationException.class);
} catch (ValidationException e) { } catch (ValidationException e) {
exception.set(Optional.of(new ResourceConflictException(e.getMessage(), e))); exception.set(Optional.of(new ResourceConflictException(e.getMessage(), e)));
return; return;
}
} }
u.setActive(false); u.setActive(false);
} }
@@ -94,13 +93,12 @@ public class SetInactiveFlag {
if (a.getAccount().isActive()) { if (a.getAccount().isActive()) {
alreadyActive.set(true); alreadyActive.set(true);
} else { } else {
for (AccountActivationValidationListener l : accountActivationValidationListeners) { try {
try { accountActivationValidationListeners.runEach(
l.validateActivation(a); l -> l.validateActivation(a), ValidationException.class);
} catch (ValidationException e) { } catch (ValidationException e) {
exception.set(Optional.of(new ResourceConflictException(e.getMessage(), e))); exception.set(Optional.of(new ResourceConflictException(e.getMessage(), e)));
return; return;
}
} }
u.setActive(true); u.setActive(true);
} }

View File

@@ -26,12 +26,13 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.StartupCheck; import com.google.gerrit.server.StartupCheck;
import com.google.gerrit.server.StartupException; import com.google.gerrit.server.StartupException;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -48,19 +49,19 @@ import org.eclipse.jgit.lib.Config;
public class UniversalGroupBackend implements GroupBackend { public class UniversalGroupBackend implements GroupBackend {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<GroupBackend> backends; private final PluginSetContext<GroupBackend> backends;
@Inject @Inject
UniversalGroupBackend(DynamicSet<GroupBackend> backends) { UniversalGroupBackend(PluginSetContext<GroupBackend> backends) {
this.backends = backends; this.backends = backends;
} }
@Nullable @Nullable
private GroupBackend backend(AccountGroup.UUID uuid) { private GroupBackend backend(AccountGroup.UUID uuid) {
if (uuid != null) { if (uuid != null) {
for (GroupBackend g : backends) { for (PluginSetEntryContext<GroupBackend> c : backends) {
if (g.handles(uuid)) { if (c.call(b -> b.handles(uuid))) {
return g; return c.get();
} }
} }
} }
@@ -88,9 +89,7 @@ public class UniversalGroupBackend implements GroupBackend {
@Override @Override
public Collection<GroupReference> suggest(String name, ProjectState project) { public Collection<GroupReference> suggest(String name, ProjectState project) {
Set<GroupReference> groups = Sets.newTreeSet(GROUP_REF_NAME_COMPARATOR); Set<GroupReference> groups = Sets.newTreeSet(GROUP_REF_NAME_COMPARATOR);
for (GroupBackend g : backends) { backends.runEach(g -> groups.addAll(g.suggest(name, project)));
groups.addAll(g.suggest(name, project));
}
return groups; return groups;
} }
@@ -104,9 +103,7 @@ public class UniversalGroupBackend implements GroupBackend {
private UniversalGroupMembership(IdentifiedUser user) { private UniversalGroupMembership(IdentifiedUser user) {
ImmutableMap.Builder<GroupBackend, GroupMembership> builder = ImmutableMap.builder(); ImmutableMap.Builder<GroupBackend, GroupMembership> builder = ImmutableMap.builder();
for (GroupBackend g : backends) { backends.runEach(g -> builder.put(g, g.membershipsOf(user)));
builder.put(g, g.membershipsOf(user));
}
this.memberships = builder.build(); this.memberships = builder.build();
} }
@@ -200,9 +197,9 @@ public class UniversalGroupBackend implements GroupBackend {
@Override @Override
public boolean isVisibleToAll(AccountGroup.UUID uuid) { public boolean isVisibleToAll(AccountGroup.UUID uuid) {
for (GroupBackend g : backends) { for (PluginSetEntryContext<GroupBackend> c : backends) {
if (g.handles(uuid)) { if (c.call(b -> b.handles(uuid))) {
return g.isVisibleToAll(uuid); return c.call(b -> b.isVisibleToAll(uuid));
} }
} }
return false; return false;

View File

@@ -15,37 +15,32 @@
package com.google.gerrit.server.audit; package com.google.gerrit.server.audit;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.audit.group.GroupAuditListener; import com.google.gerrit.server.audit.group.GroupAuditListener;
import com.google.gerrit.server.audit.group.GroupMemberAuditEvent; import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent; import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
import com.google.gerrit.server.group.GroupAuditService; import com.google.gerrit.server.group.GroupAuditService;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.sql.Timestamp; import java.sql.Timestamp;
@Singleton @Singleton
public class AuditService implements GroupAuditService { public class AuditService implements GroupAuditService {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final PluginSetContext<AuditListener> auditListeners;
private final PluginSetContext<GroupAuditListener> groupAuditListeners;
private final DynamicSet<AuditListener> auditListeners;
private final DynamicSet<GroupAuditListener> groupAuditListeners;
@Inject @Inject
public AuditService( public AuditService(
DynamicSet<AuditListener> auditListeners, PluginSetContext<AuditListener> auditListeners,
DynamicSet<GroupAuditListener> groupAuditListeners) { PluginSetContext<GroupAuditListener> groupAuditListeners) {
this.auditListeners = auditListeners; this.auditListeners = auditListeners;
this.groupAuditListeners = groupAuditListeners; this.groupAuditListeners = groupAuditListeners;
} }
public void dispatch(AuditEvent action) { public void dispatch(AuditEvent action) {
for (AuditListener auditListener : auditListeners) { auditListeners.runEach(l -> l.onAuditableAction(action));
auditListener.onAuditableAction(action);
}
} }
@Override @Override
@@ -54,15 +49,9 @@ public class AuditService implements GroupAuditService {
AccountGroup.UUID updatedGroup, AccountGroup.UUID updatedGroup,
ImmutableSet<Account.Id> addedMembers, ImmutableSet<Account.Id> addedMembers,
Timestamp addedOn) { Timestamp addedOn) {
for (GroupAuditListener auditListener : groupAuditListeners) { GroupMemberAuditEvent event =
try { GroupMemberAuditEvent.create(actor, updatedGroup, addedMembers, addedOn);
GroupMemberAuditEvent event = groupAuditListeners.runEach(l -> l.onAddMembers(event));
GroupMemberAuditEvent.create(actor, updatedGroup, addedMembers, addedOn);
auditListener.onAddMembers(event);
} catch (RuntimeException e) {
logger.atSevere().withCause(e).log("failed to log add accounts to group event");
}
}
} }
@Override @Override
@@ -71,15 +60,9 @@ public class AuditService implements GroupAuditService {
AccountGroup.UUID updatedGroup, AccountGroup.UUID updatedGroup,
ImmutableSet<Account.Id> deletedMembers, ImmutableSet<Account.Id> deletedMembers,
Timestamp deletedOn) { Timestamp deletedOn) {
for (GroupAuditListener auditListener : groupAuditListeners) { GroupMemberAuditEvent event =
try { GroupMemberAuditEvent.create(actor, updatedGroup, deletedMembers, deletedOn);
GroupMemberAuditEvent event = groupAuditListeners.runEach(l -> l.onDeleteMembers(event));
GroupMemberAuditEvent.create(actor, updatedGroup, deletedMembers, deletedOn);
auditListener.onDeleteMembers(event);
} catch (RuntimeException e) {
logger.atSevere().withCause(e).log("failed to log delete accounts from group event");
}
}
} }
@Override @Override
@@ -88,15 +71,9 @@ public class AuditService implements GroupAuditService {
AccountGroup.UUID updatedGroup, AccountGroup.UUID updatedGroup,
ImmutableSet<AccountGroup.UUID> addedSubgroups, ImmutableSet<AccountGroup.UUID> addedSubgroups,
Timestamp addedOn) { Timestamp addedOn) {
for (GroupAuditListener auditListener : groupAuditListeners) { GroupSubgroupAuditEvent event =
try { GroupSubgroupAuditEvent.create(actor, updatedGroup, addedSubgroups, addedOn);
GroupSubgroupAuditEvent event = groupAuditListeners.runEach(l -> l.onAddSubgroups(event));
GroupSubgroupAuditEvent.create(actor, updatedGroup, addedSubgroups, addedOn);
auditListener.onAddSubgroups(event);
} catch (RuntimeException e) {
logger.atSevere().withCause(e).log("failed to log add groups to group event");
}
}
} }
@Override @Override
@@ -105,14 +82,8 @@ public class AuditService implements GroupAuditService {
AccountGroup.UUID updatedGroup, AccountGroup.UUID updatedGroup,
ImmutableSet<AccountGroup.UUID> deletedSubgroups, ImmutableSet<AccountGroup.UUID> deletedSubgroups,
Timestamp deletedOn) { Timestamp deletedOn) {
for (GroupAuditListener auditListener : groupAuditListeners) { GroupSubgroupAuditEvent event =
try { GroupSubgroupAuditEvent.create(actor, updatedGroup, deletedSubgroups, deletedOn);
GroupSubgroupAuditEvent event = groupAuditListeners.runEach(l -> l.onDeleteSubgroups(event));
GroupSubgroupAuditEvent.create(actor, updatedGroup, deletedSubgroups, deletedOn);
auditListener.onDeleteSubgroups(event);
} catch (RuntimeException e) {
logger.atSevere().withCause(e).log("failed to log delete groups from group event");
}
}
} }
} }

View File

@@ -17,6 +17,7 @@ java_library(
"//java/com/google/gerrit/reviewdb:server", "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server", "//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/ioutil", "//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/time", "//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli", "//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/ssl", "//java/com/google/gerrit/util/ssl",

View File

@@ -33,7 +33,6 @@ import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling; import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.ProblemInfo; import com.google.gerrit.extensions.common.ProblemInfo;
import com.google.gerrit.extensions.common.ProblemInfo.Status; import com.google.gerrit.extensions.common.ProblemInfo.Status;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
@@ -50,6 +49,7 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.PatchSetState; import com.google.gerrit.server.notedb.PatchSetState;
import com.google.gerrit.server.patch.PatchSetInfoFactory; import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException; import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.update.BatchUpdate; import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.BatchUpdateReviewDb; import com.google.gerrit.server.update.BatchUpdateReviewDb;
@@ -107,7 +107,7 @@ public class ConsistencyChecker {
private final ChangeNotes.Factory notesFactory; private final ChangeNotes.Factory notesFactory;
private final Accounts accounts; private final Accounts accounts;
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore; private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final PatchSetInfoFactory patchSetInfoFactory; private final PatchSetInfoFactory patchSetInfoFactory;
private final PatchSetInserter.Factory patchSetInserterFactory; private final PatchSetInserter.Factory patchSetInserterFactory;
@@ -136,7 +136,7 @@ public class ConsistencyChecker {
@GerritPersonIdent Provider<PersonIdent> serverIdent, @GerritPersonIdent Provider<PersonIdent> serverIdent,
ChangeNotes.Factory notesFactory, ChangeNotes.Factory notesFactory,
Accounts accounts, Accounts accounts,
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore, PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
PatchSetInfoFactory patchSetInfoFactory, PatchSetInfoFactory patchSetInfoFactory,
PatchSetInserter.Factory patchSetInserterFactory, PatchSetInserter.Factory patchSetInserterFactory,
@@ -668,7 +668,7 @@ public class ConsistencyChecker {
throws OrmException, PatchSetInfoNotAvailableException { throws OrmException, PatchSetInfoNotAvailableException {
// Delete dangling key references. // Delete dangling key references.
ReviewDb db = BatchUpdateReviewDb.unwrap(ctx.getDb()); ReviewDb db = BatchUpdateReviewDb.unwrap(ctx.getDb());
accountPatchReviewStore.get().clearReviewed(psId); accountPatchReviewStore.run(s -> s.clearReviewed(psId), OrmException.class);
db.changeMessages().delete(db.changeMessages().byChange(psId.getParentKey())); db.changeMessages().delete(db.changeMessages().byChange(psId.getParentKey()));
db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(psId)); db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(psId));
db.patchComments().delete(db.patchComments().byPatchSet(psId)); db.patchComments().delete(db.patchComments().byPatchSet(psId));

View File

@@ -18,12 +18,12 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder; import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.extensions.api.changes.IncludedInInfo; import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.config.ExternalIncludedIn; import com.google.gerrit.extensions.config.ExternalIncludedIn;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.io.IOException; import java.io.IOException;
@@ -37,10 +37,11 @@ import org.eclipse.jgit.revwalk.RevWalk;
@Singleton @Singleton
public class IncludedIn { public class IncludedIn {
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final DynamicSet<ExternalIncludedIn> externalIncludedIn; private final PluginSetContext<ExternalIncludedIn> externalIncludedIn;
@Inject @Inject
IncludedIn(GitRepositoryManager repoManager, DynamicSet<ExternalIncludedIn> externalIncludedIn) { IncludedIn(
GitRepositoryManager repoManager, PluginSetContext<ExternalIncludedIn> externalIncludedIn) {
this.repoManager = repoManager; this.repoManager = repoManager;
this.externalIncludedIn = externalIncludedIn; this.externalIncludedIn = externalIncludedIn;
} }
@@ -61,13 +62,15 @@ public class IncludedIn {
IncludedInResolver.Result d = IncludedInResolver.resolve(r, rw, rev); IncludedInResolver.Result d = IncludedInResolver.resolve(r, rw, rev);
ListMultimap<String, String> external = MultimapBuilder.hashKeys().arrayListValues().build(); ListMultimap<String, String> external = MultimapBuilder.hashKeys().arrayListValues().build();
for (ExternalIncludedIn ext : externalIncludedIn) { externalIncludedIn.runEach(
ListMultimap<String, String> extIncludedIns = ext -> {
ext.getIncludedIn(project.get(), rev.name(), d.tags(), d.branches()); ListMultimap<String, String> extIncludedIns =
if (extIncludedIns != null) { ext.getIncludedIn(project.get(), rev.name(), d.tags(), d.branches());
external.putAll(extIncludedIns); if (extIncludedIns != null) {
} external.putAll(extIncludedIns);
} }
});
return new IncludedInInfo( return new IncludedInInfo(
d.branches(), d.tags(), (!external.isEmpty() ? external.asMap() : null)); d.branches(), d.tags(), (!external.isEmpty() ? external.asMap() : null));
} }

View File

@@ -17,7 +17,6 @@ package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
@@ -27,6 +26,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.AssigneeChanged; import com.google.gerrit.server.extensions.events.AssigneeChanged;
import com.google.gerrit.server.mail.send.SetAssigneeSender; import com.google.gerrit.server.mail.send.SetAssigneeSender;
import com.google.gerrit.server.notedb.ChangeUpdate; import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext; import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context; import com.google.gerrit.server.update.Context;
@@ -45,7 +45,7 @@ public class SetAssigneeOp implements BatchUpdateOp {
} }
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final DynamicSet<AssigneeValidationListener> validationListeners; private final PluginSetContext<AssigneeValidationListener> validationListeners;
private final IdentifiedUser newAssignee; private final IdentifiedUser newAssignee;
private final AssigneeChanged assigneeChanged; private final AssigneeChanged assigneeChanged;
private final SetAssigneeSender.Factory setAssigneeSenderFactory; private final SetAssigneeSender.Factory setAssigneeSenderFactory;
@@ -58,7 +58,7 @@ public class SetAssigneeOp implements BatchUpdateOp {
@Inject @Inject
SetAssigneeOp( SetAssigneeOp(
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
DynamicSet<AssigneeValidationListener> validationListeners, PluginSetContext<AssigneeValidationListener> validationListeners,
AssigneeChanged assigneeChanged, AssigneeChanged assigneeChanged,
SetAssigneeSender.Factory setAssigneeSenderFactory, SetAssigneeSender.Factory setAssigneeSenderFactory,
Provider<IdentifiedUser> user, Provider<IdentifiedUser> user,
@@ -80,11 +80,10 @@ public class SetAssigneeOp implements BatchUpdateOp {
return false; return false;
} }
try { try {
for (AssigneeValidationListener validator : validationListeners) { validationListeners.runEach(
validator.validateAssignee(change, newAssignee.getAccount()); l -> l.validateAssignee(change, newAssignee.getAccount()), ValidationException.class);
}
} catch (ValidationException e) { } catch (ValidationException e) {
throw new ResourceConflictException(e.getMessage()); throw new ResourceConflictException(e.getMessage(), e);
} }
if (change.getAssignee() != null) { if (change.getAssignee() != null) {

View File

@@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.HashtagsInput; import com.google.gerrit.extensions.api.changes.HashtagsInput;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -34,6 +33,7 @@ import com.google.gerrit.server.extensions.events.HashtagsEdited;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate; import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.NotesMigration; import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext; import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context; import com.google.gerrit.server.update.Context;
@@ -54,7 +54,7 @@ public class SetHashtagsOp implements BatchUpdateOp {
private final NotesMigration notesMigration; private final NotesMigration notesMigration;
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final DynamicSet<HashtagValidationListener> validationListeners; private final PluginSetContext<HashtagValidationListener> validationListeners;
private final HashtagsEdited hashtagsEdited; private final HashtagsEdited hashtagsEdited;
private final HashtagsInput input; private final HashtagsInput input;
@@ -69,7 +69,7 @@ public class SetHashtagsOp implements BatchUpdateOp {
SetHashtagsOp( SetHashtagsOp(
NotesMigration notesMigration, NotesMigration notesMigration,
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
DynamicSet<HashtagValidationListener> validationListeners, PluginSetContext<HashtagValidationListener> validationListeners,
HashtagsEdited hashtagsEdited, HashtagsEdited hashtagsEdited,
@Assisted @Nullable HashtagsInput input) { @Assisted @Nullable HashtagsInput input) {
this.notesMigration = notesMigration; this.notesMigration = notesMigration;
@@ -106,10 +106,8 @@ public class SetHashtagsOp implements BatchUpdateOp {
toAdd = new HashSet<>(extractTags(input.add)); toAdd = new HashSet<>(extractTags(input.add));
toRemove = new HashSet<>(extractTags(input.remove)); toRemove = new HashSet<>(extractTags(input.remove));
for (HashtagValidationListener validator : validationListeners) { validationListeners.runEach(
validator.validateHashtags(update.getChange(), toAdd, toRemove); l -> l.validateHashtags(update.getChange(), toAdd, toRemove), ValidationException.class);
}
updated.addAll(existingHashtags); updated.addAll(existingHashtags);
toAdd.removeAll(existingHashtags); toAdd.removeAll(existingHashtags);
toRemove.retainAll(existingHashtags); toRemove.retainAll(existingHashtags);
@@ -123,7 +121,7 @@ public class SetHashtagsOp implements BatchUpdateOp {
updatedHashtags = ImmutableSortedSet.copyOf(updated); updatedHashtags = ImmutableSortedSet.copyOf(updated);
return true; return true;
} catch (ValidationException | InvalidHashtagException e) { } catch (ValidationException | InvalidHashtagException e) {
throw new BadRequestException(e.getMessage()); throw new BadRequestException(e.getMessage(), e);
} }
} }

View File

@@ -16,7 +16,6 @@ package com.google.gerrit.server.events;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
@@ -31,6 +30,8 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission; import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@@ -53,10 +54,10 @@ public class EventBroker implements EventDispatcher {
} }
/** Listeners to receive changes as they happen (limited by visibility of user). */ /** Listeners to receive changes as they happen (limited by visibility of user). */
protected final DynamicSet<UserScopedEventListener> listeners; protected final PluginSetContext<UserScopedEventListener> listeners;
/** Listeners to receive all changes as they happen. */ /** Listeners to receive all changes as they happen. */
protected final DynamicSet<EventListener> unrestrictedListeners; protected final PluginSetContext<EventListener> unrestrictedListeners;
private final PermissionBackend permissionBackend; private final PermissionBackend permissionBackend;
protected final ProjectCache projectCache; protected final ProjectCache projectCache;
@@ -67,8 +68,8 @@ public class EventBroker implements EventDispatcher {
@Inject @Inject
public EventBroker( public EventBroker(
DynamicSet<UserScopedEventListener> listeners, PluginSetContext<UserScopedEventListener> listeners,
DynamicSet<EventListener> unrestrictedListeners, PluginSetContext<EventListener> unrestrictedListeners,
PermissionBackend permissionBackend, PermissionBackend permissionBackend,
ProjectCache projectCache, ProjectCache projectCache,
ChangeNotes.Factory notesFactory, ChangeNotes.Factory notesFactory,
@@ -104,25 +105,25 @@ public class EventBroker implements EventDispatcher {
} }
protected void fireEventForUnrestrictedListeners(Event event) { protected void fireEventForUnrestrictedListeners(Event event) {
for (EventListener listener : unrestrictedListeners) { unrestrictedListeners.runEach(l -> l.onEvent(event));
listener.onEvent(event);
}
} }
protected void fireEvent(Change change, ChangeEvent event) protected void fireEvent(Change change, ChangeEvent event)
throws OrmException, PermissionBackendException { throws OrmException, PermissionBackendException {
for (UserScopedEventListener listener : listeners) { for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
if (isVisibleTo(change, listener.getUser())) { CurrentUser user = c.call(l -> l.getUser());
listener.onEvent(event); if (isVisibleTo(change, user)) {
c.run(l -> l.onEvent(event));
} }
} }
fireEventForUnrestrictedListeners(event); fireEventForUnrestrictedListeners(event);
} }
protected void fireEvent(Project.NameKey project, ProjectEvent event) { protected void fireEvent(Project.NameKey project, ProjectEvent event) {
for (UserScopedEventListener listener : listeners) { for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
if (isVisibleTo(project, listener.getUser())) { CurrentUser user = c.call(l -> l.getUser());
listener.onEvent(event); if (isVisibleTo(project, user)) {
c.run(l -> l.onEvent(event));
} }
} }
fireEventForUnrestrictedListeners(event); fireEventForUnrestrictedListeners(event);
@@ -130,18 +131,20 @@ public class EventBroker implements EventDispatcher {
protected void fireEvent(Branch.NameKey branchName, RefEvent event) protected void fireEvent(Branch.NameKey branchName, RefEvent event)
throws PermissionBackendException { throws PermissionBackendException {
for (UserScopedEventListener listener : listeners) { for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
if (isVisibleTo(branchName, listener.getUser())) { CurrentUser user = c.call(l -> l.getUser());
listener.onEvent(event); if (isVisibleTo(branchName, user)) {
c.run(l -> l.onEvent(event));
} }
} }
fireEventForUnrestrictedListeners(event); fireEventForUnrestrictedListeners(event);
} }
protected void fireEvent(Event event) throws OrmException, PermissionBackendException { protected void fireEvent(Event event) throws OrmException, PermissionBackendException {
for (UserScopedEventListener listener : listeners) { for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
if (isVisibleTo(event, listener.getUser())) { CurrentUser user = c.call(l -> l.getUser());
listener.onEvent(event); if (isVisibleTo(event, user)) {
c.run(l -> l.onEvent(event));
} }
} }
fireEventForUnrestrictedListeners(event); fireEventForUnrestrictedListeners(event);

View File

@@ -40,7 +40,6 @@ import com.google.gerrit.extensions.events.RevisionCreatedListener;
import com.google.gerrit.extensions.events.TopicEditedListener; import com.google.gerrit.extensions.events.TopicEditedListener;
import com.google.gerrit.extensions.events.VoteDeletedListener; import com.google.gerrit.extensions.events.VoteDeletedListener;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener; import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
@@ -55,7 +54,7 @@ import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute; import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
@@ -117,7 +116,7 @@ public class StreamEventsApiListener
} }
} }
private final DynamicItem<EventDispatcher> dispatcher; private final PluginItemContext<EventDispatcher> dispatcher;
private final Provider<ReviewDb> db; private final Provider<ReviewDb> db;
private final EventFactory eventFactory; private final EventFactory eventFactory;
private final ProjectCache projectCache; private final ProjectCache projectCache;
@@ -127,7 +126,7 @@ public class StreamEventsApiListener
@Inject @Inject
StreamEventsApiListener( StreamEventsApiListener(
DynamicItem<EventDispatcher> dispatcher, PluginItemContext<EventDispatcher> dispatcher,
Provider<ReviewDb> db, Provider<ReviewDb> db,
EventFactory eventFactory, EventFactory eventFactory,
ProjectCache projectCache, ProjectCache projectCache,
@@ -264,8 +263,8 @@ public class StreamEventsApiListener
event.changer = accountAttributeSupplier(ev.getWho()); event.changer = accountAttributeSupplier(ev.getWho());
event.oldAssignee = accountAttributeSupplier(ev.getOldAssignee()); event.oldAssignee = accountAttributeSupplier(ev.getOldAssignee());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -281,8 +280,8 @@ public class StreamEventsApiListener
event.changer = accountAttributeSupplier(ev.getWho()); event.changer = accountAttributeSupplier(ev.getWho());
event.oldTopic = ev.getOldTopic(); event.oldTopic = ev.getOldTopic();
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -299,8 +298,8 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, patchSet); event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.uploader = accountAttributeSupplier(ev.getWho()); event.uploader = accountAttributeSupplier(ev.getWho());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -319,8 +318,8 @@ public class StreamEventsApiListener
event.approvals = event.approvals =
approvalsAttributeSupplier(change, ev.getNewApprovals(), ev.getOldApprovals()); approvalsAttributeSupplier(change, ev.getNewApprovals(), ev.getOldApprovals());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -336,9 +335,9 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes)); event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
for (AccountInfo reviewer : ev.getReviewers()) { for (AccountInfo reviewer : ev.getReviewers()) {
event.reviewer = accountAttributeSupplier(reviewer); event.reviewer = accountAttributeSupplier(reviewer);
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(event));
} }
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -349,7 +348,7 @@ public class StreamEventsApiListener
event.projectName = ev.getProjectName(); event.projectName = ev.getProjectName();
event.headName = ev.getHeadName(); event.headName = ev.getHeadName();
dispatcher.get().postEvent(event.getProjectNameKey(), event); dispatcher.run(d -> d.postEvent(event.getProjectNameKey(), event));
} }
@Override @Override
@@ -365,8 +364,8 @@ public class StreamEventsApiListener
event.added = hashtagArray(ev.getAddedHashtags()); event.added = hashtagArray(ev.getAddedHashtags());
event.removed = hashtagArray(ev.getRemovedHashtags()); event.removed = hashtagArray(ev.getRemovedHashtags());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -389,11 +388,7 @@ public class StreamEventsApiListener
refName); refName);
} }
}); });
try { dispatcher.run(d -> d.postEvent(refName, event));
dispatcher.get().postEvent(refName, event);
} catch (PermissionBackendException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
} }
@Override @Override
@@ -410,8 +405,8 @@ public class StreamEventsApiListener
event.comment = ev.getComment(); event.comment = ev.getComment();
event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals()); event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -428,8 +423,8 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes)); event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
event.reason = ev.getReason(); event.reason = ev.getReason();
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -446,8 +441,8 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes)); event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
event.newRev = ev.getNewRevisionId(); event.newRev = ev.getNewRevisionId();
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -464,8 +459,8 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes)); event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
event.reason = ev.getReason(); event.reason = ev.getReason();
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -482,8 +477,8 @@ public class StreamEventsApiListener
event.changer = accountAttributeSupplier(ev.getWho()); event.changer = accountAttributeSupplier(ev.getWho());
event.patchSet = patchSetAttributeSupplier(change, patchSet); event.patchSet = patchSetAttributeSupplier(change, patchSet);
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -500,8 +495,8 @@ public class StreamEventsApiListener
event.changer = accountAttributeSupplier(ev.getWho()); event.changer = accountAttributeSupplier(ev.getWho());
event.patchSet = patchSetAttributeSupplier(change, patchSet); event.patchSet = patchSetAttributeSupplier(change, patchSet);
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -520,8 +515,8 @@ public class StreamEventsApiListener
event.remover = accountAttributeSupplier(ev.getWho()); event.remover = accountAttributeSupplier(ev.getWho());
event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals()); event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }
@@ -536,8 +531,8 @@ public class StreamEventsApiListener
event.change = changeAttributeSupplier(change, notes); event.change = changeAttributeSupplier(change, notes);
event.deleter = accountAttributeSupplier(ev.getWho()); event.deleter = accountAttributeSupplier(ev.getWho());
dispatcher.get().postEvent(change, event); dispatcher.run(d -> d.postEvent(change, event));
} catch (OrmException | PermissionBackendException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event"); logger.atSevere().withCause(e).log("Failed to dispatch event");
} }
} }

View File

@@ -16,34 +16,28 @@ package com.google.gerrit.server.extensions.events;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.events.AgreementSignupListener; import com.google.gerrit.extensions.events.AgreementSignupListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@Singleton @Singleton
public class AgreementSignup { public class AgreementSignup {
private final DynamicSet<AgreementSignupListener> listeners; private final PluginSetContext<AgreementSignupListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
AgreementSignup(DynamicSet<AgreementSignupListener> listeners, EventUtil util) { AgreementSignup(PluginSetContext<AgreementSignupListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire(AccountState accountState, String agreementName) { public void fire(AccountState accountState, String agreementName) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
Event event = new Event(util.accountInfo(accountState), agreementName); Event event = new Event(util.accountInfo(accountState), agreementName);
for (AgreementSignupListener l : listeners) { listeners.runEach(l -> l.onAgreementSignup(event));
try {
l.onAgreementSignup(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} }
private static class Event extends AbstractNoNotifyEvent private static class Event extends AbstractNoNotifyEvent

View File

@@ -19,9 +19,9 @@ import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.AssigneeChangedListener; import com.google.gerrit.extensions.events.AssigneeChangedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -31,18 +31,18 @@ import java.sql.Timestamp;
public class AssigneeChanged { public class AssigneeChanged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<AssigneeChangedListener> listeners; private final PluginSetContext<AssigneeChangedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
AssigneeChanged(DynamicSet<AssigneeChangedListener> listeners, EventUtil util) { AssigneeChanged(PluginSetContext<AssigneeChangedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire( public void fire(
Change change, AccountState accountState, AccountState oldAssignee, Timestamp when) { Change change, AccountState accountState, AccountState oldAssignee, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -52,13 +52,7 @@ public class AssigneeChanged {
util.accountInfo(accountState), util.accountInfo(accountState),
util.accountInfo(oldAssignee), util.accountInfo(oldAssignee),
when); when);
for (AssigneeChangedListener l : listeners) { listeners.runEach(l -> l.onAssigneeChanged(event));
try {
l.onAssigneeChanged(event);
} catch (Exception e) {
util.logEventListenerError(event, l, e);
}
}
} catch (OrmException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Couldn't fire event"); logger.atSevere().withCause(e).log("Couldn't fire event");
} }

View File

@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeAbandonedListener; import com.google.gerrit.extensions.events.ChangeAbandonedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -28,6 +27,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -38,11 +38,11 @@ import java.sql.Timestamp;
public class ChangeAbandoned { public class ChangeAbandoned {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ChangeAbandonedListener> listeners; private final PluginSetContext<ChangeAbandonedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ChangeAbandoned(DynamicSet<ChangeAbandonedListener> listeners, EventUtil util) { ChangeAbandoned(PluginSetContext<ChangeAbandonedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -54,7 +54,7 @@ public class ChangeAbandoned {
String reason, String reason,
Timestamp when, Timestamp when,
NotifyHandling notifyHandling) { NotifyHandling notifyHandling) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -66,13 +66,7 @@ public class ChangeAbandoned {
reason, reason,
when, when,
notifyHandling); notifyHandling);
for (ChangeAbandonedListener l : listeners) { listeners.runEach(l -> l.onChangeAbandoned(event));
try {
l.onChangeAbandoned(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -19,9 +19,9 @@ import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.ChangeDeletedListener; import com.google.gerrit.extensions.events.ChangeDeletedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -31,28 +31,22 @@ import java.sql.Timestamp;
public class ChangeDeleted { public class ChangeDeleted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ChangeDeletedListener> listeners; private final PluginSetContext<ChangeDeletedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ChangeDeleted(DynamicSet<ChangeDeletedListener> listeners, EventUtil util) { ChangeDeleted(PluginSetContext<ChangeDeletedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire(Change change, AccountState deleter, Timestamp when) { public void fire(Change change, AccountState deleter, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
Event event = new Event(util.changeInfo(change), util.accountInfo(deleter), when); Event event = new Event(util.changeInfo(change), util.accountInfo(deleter), when);
for (ChangeDeletedListener l : listeners) { listeners.runEach(l -> l.onChangeDeleted(event));
try {
l.onChangeDeleted(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (OrmException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Couldn't fire event"); logger.atSevere().withCause(e).log("Couldn't fire event");
} }

View File

@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeMergedListener; import com.google.gerrit.extensions.events.ChangeMergedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -28,6 +27,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -38,18 +38,18 @@ import java.sql.Timestamp;
public class ChangeMerged { public class ChangeMerged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ChangeMergedListener> listeners; private final PluginSetContext<ChangeMergedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ChangeMerged(DynamicSet<ChangeMergedListener> listeners, EventUtil util) { ChangeMerged(PluginSetContext<ChangeMergedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire( public void fire(
Change change, PatchSet ps, AccountState merger, String newRevisionId, Timestamp when) { Change change, PatchSet ps, AccountState merger, String newRevisionId, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -60,13 +60,7 @@ public class ChangeMerged {
util.accountInfo(merger), util.accountInfo(merger),
newRevisionId, newRevisionId,
when); when);
for (ChangeMergedListener l : listeners) { listeners.runEach(l -> l.onChangeMerged(event));
try {
l.onChangeMerged(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeRestoredListener; import com.google.gerrit.extensions.events.ChangeRestoredListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -28,6 +27,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -38,18 +38,18 @@ import java.sql.Timestamp;
public class ChangeRestored { public class ChangeRestored {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ChangeRestoredListener> listeners; private final PluginSetContext<ChangeRestoredListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ChangeRestored(DynamicSet<ChangeRestoredListener> listeners, EventUtil util) { ChangeRestored(PluginSetContext<ChangeRestoredListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire( public void fire(
Change change, PatchSet ps, AccountState restorer, String reason, Timestamp when) { Change change, PatchSet ps, AccountState restorer, String reason, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -60,13 +60,7 @@ public class ChangeRestored {
util.accountInfo(restorer), util.accountInfo(restorer),
reason, reason,
when); when);
for (ChangeRestoredListener l : listeners) { listeners.runEach(l -> l.onChangeRestored(event));
try {
l.onChangeRestored(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -18,8 +18,8 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.changes.NotifyHandling; import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.ChangeRevertedListener; import com.google.gerrit.extensions.events.ChangeRevertedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -29,28 +29,22 @@ import java.sql.Timestamp;
public class ChangeReverted { public class ChangeReverted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ChangeRevertedListener> listeners; private final PluginSetContext<ChangeRevertedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ChangeReverted(DynamicSet<ChangeRevertedListener> listeners, EventUtil util) { ChangeReverted(PluginSetContext<ChangeRevertedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire(Change change, Change revertChange, Timestamp when) { public void fire(Change change, Change revertChange, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
Event event = new Event(util.changeInfo(change), util.changeInfo(revertChange), when); Event event = new Event(util.changeInfo(change), util.changeInfo(revertChange), when);
for (ChangeRevertedListener l : listeners) { listeners.runEach(l -> l.onChangeReverted(event));
try {
l.onChangeReverted(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (OrmException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Couldn't fire event"); logger.atSevere().withCause(e).log("Couldn't fire event");
} }

View File

@@ -21,7 +21,6 @@ import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.CommentAddedListener; import com.google.gerrit.extensions.events.CommentAddedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -29,6 +28,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -40,11 +40,11 @@ import java.util.Map;
public class CommentAdded { public class CommentAdded {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<CommentAddedListener> listeners; private final PluginSetContext<CommentAddedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
CommentAdded(DynamicSet<CommentAddedListener> listeners, EventUtil util) { CommentAdded(PluginSetContext<CommentAddedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -57,7 +57,7 @@ public class CommentAdded {
Map<String, Short> approvals, Map<String, Short> approvals,
Map<String, Short> oldApprovals, Map<String, Short> oldApprovals,
Timestamp when) { Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -70,13 +70,7 @@ public class CommentAdded {
util.approvals(author, approvals, when), util.approvals(author, approvals, when),
util.approvals(author, oldApprovals, when), util.approvals(author, oldApprovals, when),
when); when);
for (CommentAddedListener l : listeners) { listeners.runEach(l -> l.onCommentAdded(event));
try {
l.onCommentAdded(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -16,7 +16,6 @@ package com.google.gerrit.server.extensions.events;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ApprovalInfo; import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -45,8 +44,6 @@ import java.util.Map;
@Singleton @Singleton
public class EventUtil { public class EventUtil {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final ImmutableSet<ListChangesOption> CHANGE_OPTIONS; private static final ImmutableSet<ListChangesOption> CHANGE_OPTIONS;
static { static {
@@ -118,22 +115,4 @@ public class EventUtil {
} }
return result; return result;
} }
public void logEventListenerError(Object event, Object listener, Exception error) {
logger.atWarning().log(
"Error in event listener %s for event %s: %s - %s",
listener.getClass().getName(),
event.getClass().getName(),
error.getClass().getName(),
error.getMessage());
logger.atFine().withCause(error).log(
"Cause of error in event listener %s:", listener.getClass().getName());
}
public static void logEventListenerError(Object listener, Exception error) {
logger.atWarning().log(
"Error in event listener %s: %s", listener.getClass().getName(), error.getMessage());
logger.atFine().withCause(error).log(
"Cause of error in event listener %s", listener.getClass().getName());
}
} }

View File

@@ -17,9 +17,9 @@ package com.google.gerrit.server.extensions.events;
import com.google.gerrit.extensions.api.changes.NotifyHandling; import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.BatchRefUpdate;
@@ -57,11 +57,11 @@ public class GitReferenceUpdated {
Project.NameKey project, BatchRefUpdate batchRefUpdate, AccountState updater) {} Project.NameKey project, BatchRefUpdate batchRefUpdate, AccountState updater) {}
}; };
private final DynamicSet<GitReferenceUpdatedListener> listeners; private final PluginSetContext<GitReferenceUpdatedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
GitReferenceUpdated(DynamicSet<GitReferenceUpdatedListener> listeners, EventUtil util) { GitReferenceUpdated(PluginSetContext<GitReferenceUpdatedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -121,7 +121,7 @@ public class GitReferenceUpdated {
} }
public void fire(Project.NameKey project, BatchRefUpdate batchRefUpdate, AccountState updater) { public void fire(Project.NameKey project, BatchRefUpdate batchRefUpdate, AccountState updater) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
for (ReceiveCommand cmd : batchRefUpdate.getCommands()) { for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
@@ -144,19 +144,13 @@ public class GitReferenceUpdated {
ObjectId newObjectId, ObjectId newObjectId,
ReceiveCommand.Type type, ReceiveCommand.Type type,
AccountInfo updater) { AccountInfo updater) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
ObjectId o = oldObjectId != null ? oldObjectId : ObjectId.zeroId(); ObjectId o = oldObjectId != null ? oldObjectId : ObjectId.zeroId();
ObjectId n = newObjectId != null ? newObjectId : ObjectId.zeroId(); ObjectId n = newObjectId != null ? newObjectId : ObjectId.zeroId();
Event event = new Event(project, ref, o.name(), n.name(), type, updater); Event event = new Event(project, ref, o.name(), n.name(), type, updater);
for (GitReferenceUpdatedListener l : listeners) { listeners.runEach(l -> l.onGitReferenceUpdated(event));
try {
l.onGitReferenceUpdated(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} }
public static class Event implements GitReferenceUpdatedListener.Event { public static class Event implements GitReferenceUpdatedListener.Event {

View File

@@ -20,9 +20,9 @@ import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.HashtagsEditedListener; import com.google.gerrit.extensions.events.HashtagsEditedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -34,11 +34,11 @@ import java.util.Set;
public class HashtagsEdited { public class HashtagsEdited {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<HashtagsEditedListener> listeners; private final PluginSetContext<HashtagsEditedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
public HashtagsEdited(DynamicSet<HashtagsEditedListener> listeners, EventUtil util) { public HashtagsEdited(PluginSetContext<HashtagsEditedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -50,20 +50,14 @@ public class HashtagsEdited {
Set<String> added, Set<String> added,
Set<String> removed, Set<String> removed,
Timestamp when) { Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
Event event = Event event =
new Event( new Event(
util.changeInfo(change), util.accountInfo(editor), hashtags, added, removed, when); util.changeInfo(change), util.accountInfo(editor), hashtags, added, removed, when);
for (HashtagsEditedListener l : listeners) { listeners.runEach(l -> l.onHashtagsEdited(event));
try {
l.onHashtagsEdited(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (OrmException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Couldn't fire event"); logger.atSevere().withCause(e).log("Couldn't fire event");
} }

View File

@@ -20,13 +20,13 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.PrivateStateChangedListener; import com.google.gerrit.extensions.events.PrivateStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -37,17 +37,17 @@ import java.sql.Timestamp;
public class PrivateStateChanged { public class PrivateStateChanged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<PrivateStateChangedListener> listeners; private final PluginSetContext<PrivateStateChangedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
PrivateStateChanged(DynamicSet<PrivateStateChangedListener> listeners, EventUtil util) { PrivateStateChanged(PluginSetContext<PrivateStateChangedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire(Change change, PatchSet patchSet, AccountState account, Timestamp when) { public void fire(Change change, PatchSet patchSet, AccountState account, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -57,13 +57,7 @@ public class PrivateStateChanged {
util.revisionInfo(change.getProject(), patchSet), util.revisionInfo(change.getProject(), patchSet),
util.accountInfo(account), util.accountInfo(account),
when); when);
for (PrivateStateChangedListener l : listeners) { listeners.runEach(l -> l.onPrivateStateChanged(event));
try {
l.onPrivateStateChanged(event);
} catch (Exception e) {
util.logEventListenerError(event, l, e);
}
}
} catch (OrmException } catch (OrmException
| PatchListNotAvailableException | PatchListNotAvailableException
| GpgException | GpgException

View File

@@ -21,7 +21,6 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ReviewerAddedListener; import com.google.gerrit.extensions.events.ReviewerAddedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -29,6 +28,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -40,11 +40,11 @@ import java.util.List;
public class ReviewerAdded { public class ReviewerAdded {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ReviewerAddedListener> listeners; private final PluginSetContext<ReviewerAddedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ReviewerAdded(DynamicSet<ReviewerAddedListener> listeners, EventUtil util) { ReviewerAdded(PluginSetContext<ReviewerAddedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -55,7 +55,7 @@ public class ReviewerAdded {
List<AccountState> reviewers, List<AccountState> reviewers,
AccountState adder, AccountState adder,
Timestamp when) { Timestamp when) {
if (!listeners.iterator().hasNext() || reviewers.isEmpty()) { if (listeners.isEmpty() || reviewers.isEmpty()) {
return; return;
} }
@@ -67,13 +67,7 @@ public class ReviewerAdded {
Lists.transform(reviewers, util::accountInfo), Lists.transform(reviewers, util::accountInfo),
util.accountInfo(adder), util.accountInfo(adder),
when); when);
for (ReviewerAddedListener l : listeners) { listeners.runEach(l -> l.onReviewersAdded(event));
try {
l.onReviewersAdded(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -21,7 +21,6 @@ import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ReviewerDeletedListener; import com.google.gerrit.extensions.events.ReviewerDeletedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -29,6 +28,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -40,11 +40,11 @@ import java.util.Map;
public class ReviewerDeleted { public class ReviewerDeleted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<ReviewerDeletedListener> listeners; private final PluginSetContext<ReviewerDeletedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
ReviewerDeleted(DynamicSet<ReviewerDeletedListener> listeners, EventUtil util) { ReviewerDeleted(PluginSetContext<ReviewerDeletedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -59,7 +59,7 @@ public class ReviewerDeleted {
Map<String, Short> oldApprovals, Map<String, Short> oldApprovals,
NotifyHandling notify, NotifyHandling notify,
Timestamp when) { Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -74,13 +74,7 @@ public class ReviewerDeleted {
util.approvals(reviewer, oldApprovals, when), util.approvals(reviewer, oldApprovals, when),
notify, notify,
when); when);
for (ReviewerDeletedListener listener : listeners) { listeners.runEach(l -> l.onReviewerDeleted(event));
try {
listener.onReviewerDeleted(event);
} catch (Exception e) {
util.logEventListenerError(this, listener, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.RevisionCreatedListener; import com.google.gerrit.extensions.events.RevisionCreatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -28,6 +27,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -49,11 +49,11 @@ public class RevisionCreated {
NotifyHandling notify) {} NotifyHandling notify) {}
}; };
private final DynamicSet<RevisionCreatedListener> listeners; private final PluginSetContext<RevisionCreatedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
RevisionCreated(DynamicSet<RevisionCreatedListener> listeners, EventUtil util) { RevisionCreated(PluginSetContext<RevisionCreatedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -69,7 +69,7 @@ public class RevisionCreated {
AccountState uploader, AccountState uploader,
Timestamp when, Timestamp when,
NotifyHandling notify) { NotifyHandling notify) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -80,13 +80,7 @@ public class RevisionCreated {
util.accountInfo(uploader), util.accountInfo(uploader),
when, when,
notify); notify);
for (RevisionCreatedListener l : listeners) { listeners.runEach(l -> l.onRevisionCreated(event));
try {
l.onRevisionCreated(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -19,9 +19,9 @@ import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.TopicEditedListener; import com.google.gerrit.extensions.events.TopicEditedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -31,29 +31,23 @@ import java.sql.Timestamp;
public class TopicEdited { public class TopicEdited {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<TopicEditedListener> listeners; private final PluginSetContext<TopicEditedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
TopicEdited(DynamicSet<TopicEditedListener> listeners, EventUtil util) { TopicEdited(PluginSetContext<TopicEditedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire(Change change, AccountState account, String oldTopicName, Timestamp when) { public void fire(Change change, AccountState account, String oldTopicName, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
Event event = Event event =
new Event(util.changeInfo(change), util.accountInfo(account), oldTopicName, when); new Event(util.changeInfo(change), util.accountInfo(account), oldTopicName, when);
for (TopicEditedListener l : listeners) { listeners.runEach(l -> l.onTopicEdited(event));
try {
l.onTopicEdited(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (OrmException e) { } catch (OrmException e) {
logger.atSevere().withCause(e).log("Couldn't fire event"); logger.atSevere().withCause(e).log("Couldn't fire event");
} }

View File

@@ -21,7 +21,6 @@ import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.VoteDeletedListener; import com.google.gerrit.extensions.events.VoteDeletedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
@@ -29,6 +28,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -40,11 +40,11 @@ import java.util.Map;
public class VoteDeleted { public class VoteDeleted {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<VoteDeletedListener> listeners; private final PluginSetContext<VoteDeletedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
VoteDeleted(DynamicSet<VoteDeletedListener> listeners, EventUtil util) { VoteDeleted(PluginSetContext<VoteDeletedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
@@ -59,7 +59,7 @@ public class VoteDeleted {
String message, String message,
AccountState remover, AccountState remover,
Timestamp when) { Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -74,13 +74,7 @@ public class VoteDeleted {
message, message,
util.accountInfo(remover), util.accountInfo(remover),
when); when);
for (VoteDeletedListener l : listeners) { listeners.runEach(l -> l.onVoteDeleted(event));
try {
l.onVoteDeleted(event);
} catch (Exception e) {
util.logEventListenerError(this, l, e);
}
}
} catch (PatchListObjectTooLargeException e) { } catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage()); logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException } catch (PatchListNotAvailableException

View File

@@ -20,13 +20,13 @@ import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener; import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException; import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -37,18 +37,18 @@ import java.sql.Timestamp;
public class WorkInProgressStateChanged { public class WorkInProgressStateChanged {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<WorkInProgressStateChangedListener> listeners; private final PluginSetContext<WorkInProgressStateChangedListener> listeners;
private final EventUtil util; private final EventUtil util;
@Inject @Inject
WorkInProgressStateChanged( WorkInProgressStateChanged(
DynamicSet<WorkInProgressStateChangedListener> listeners, EventUtil util) { PluginSetContext<WorkInProgressStateChangedListener> listeners, EventUtil util) {
this.listeners = listeners; this.listeners = listeners;
this.util = util; this.util = util;
} }
public void fire(Change change, PatchSet patchSet, AccountState account, Timestamp when) { public void fire(Change change, PatchSet patchSet, AccountState account, Timestamp when) {
if (!listeners.iterator().hasNext()) { if (listeners.isEmpty()) {
return; return;
} }
try { try {
@@ -58,13 +58,7 @@ public class WorkInProgressStateChanged {
util.revisionInfo(change.getProject(), patchSet), util.revisionInfo(change.getProject(), patchSet),
util.accountInfo(account), util.accountInfo(account),
when); when);
for (WorkInProgressStateChangedListener l : listeners) { listeners.runEach(l -> l.onWorkInProgressStateChanged(event));
try {
l.onWorkInProgressStateChanged(event);
} catch (Exception e) {
util.logEventListenerError(event, l, e);
}
}
} catch (OrmException } catch (OrmException
| PatchListNotAvailableException | PatchListNotAvailableException
| GpgException | GpgException

View File

@@ -70,7 +70,6 @@ import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo; import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -129,6 +128,7 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.PermissionDeniedException; import com.google.gerrit.server.permissions.PermissionDeniedException;
import com.google.gerrit.server.permissions.ProjectPermission; import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.CreateRefControl; import com.google.gerrit.server.project.CreateRefControl;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
@@ -301,7 +301,7 @@ class ReceiveCommits {
private final CreateGroupPermissionSyncer createGroupPermissionSyncer; private final CreateGroupPermissionSyncer createGroupPermissionSyncer;
private final CreateRefControl createRefControl; private final CreateRefControl createRefControl;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries; private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
private final DynamicSet<ReceivePackInitializer> initializers; private final PluginSetContext<ReceivePackInitializer> initializers;
private final MergedByPushOp.Factory mergedByPushOpFactory; private final MergedByPushOp.Factory mergedByPushOpFactory;
private final NotesMigration notesMigration; private final NotesMigration notesMigration;
private final PatchSetInfoFactory patchSetInfoFactory; private final PatchSetInfoFactory patchSetInfoFactory;
@@ -376,7 +376,7 @@ class ReceiveCommits {
CreateGroupPermissionSyncer createGroupPermissionSyncer, CreateGroupPermissionSyncer createGroupPermissionSyncer,
CreateRefControl createRefControl, CreateRefControl createRefControl,
DynamicMap<ProjectConfigEntry> pluginConfigEntries, DynamicMap<ProjectConfigEntry> pluginConfigEntries,
DynamicSet<ReceivePackInitializer> initializers, PluginSetContext<ReceivePackInitializer> initializers,
MergedByPushOp.Factory mergedByPushOpFactory, MergedByPushOp.Factory mergedByPushOpFactory,
NotesMigration notesMigration, NotesMigration notesMigration,
PatchSetInfoFactory patchSetInfoFactory, PatchSetInfoFactory patchSetInfoFactory,
@@ -472,9 +472,7 @@ class ReceiveCommits {
} }
void init() { void init() {
for (ReceivePackInitializer i : initializers) { initializers.runEach(i -> i.init(projectState.getNameKey(), receivePack));
i.init(projectState.getNameKey(), receivePack);
}
} }
MessageSender getMessageSender() { MessageSender getMessageSender() {

View File

@@ -28,7 +28,6 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants; import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo; import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.BooleanProjectConfig; import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
@@ -49,6 +48,7 @@ import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@@ -89,7 +89,7 @@ public class CommitValidators {
public static class Factory { public static class Factory {
private final PersonIdent gerritIdent; private final PersonIdent gerritIdent;
private final UrlFormatter urlFormatter; private final UrlFormatter urlFormatter;
private final DynamicSet<CommitValidationListener> pluginValidators; private final PluginSetContext<CommitValidationListener> pluginValidators;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final AllUsersName allUsers; private final AllUsersName allUsers;
private final AllProjectsName allProjects; private final AllProjectsName allProjects;
@@ -103,7 +103,7 @@ public class CommitValidators {
@GerritPersonIdent PersonIdent gerritIdent, @GerritPersonIdent PersonIdent gerritIdent,
UrlFormatter urlFormatter, UrlFormatter urlFormatter,
@GerritServerConfig Config cfg, @GerritServerConfig Config cfg,
DynamicSet<CommitValidationListener> pluginValidators, PluginSetContext<CommitValidationListener> pluginValidators,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
AllUsersName allUsers, AllUsersName allUsers,
AllProjectsName allProjects, AllProjectsName allProjects,
@@ -462,10 +462,10 @@ public class CommitValidators {
/** Execute commit validation plug-ins */ /** Execute commit validation plug-ins */
public static class PluginCommitValidationListener implements CommitValidationListener { public static class PluginCommitValidationListener implements CommitValidationListener {
private final DynamicSet<CommitValidationListener> commitValidationListeners; private final PluginSetContext<CommitValidationListener> commitValidationListeners;
public PluginCommitValidationListener( public PluginCommitValidationListener(
final DynamicSet<CommitValidationListener> commitValidationListeners) { final PluginSetContext<CommitValidationListener> commitValidationListeners) {
this.commitValidationListeners = commitValidationListeners; this.commitValidationListeners = commitValidationListeners;
} }
@@ -473,14 +473,12 @@ public class CommitValidators {
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent) public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
throws CommitValidationException { throws CommitValidationException {
List<CommitValidationMessage> messages = new ArrayList<>(); List<CommitValidationMessage> messages = new ArrayList<>();
try {
for (CommitValidationListener validator : commitValidationListeners) { commitValidationListeners.runEach(
try { l -> l.onCommitReceived(receiveEvent), CommitValidationException.class);
messages.addAll(validator.onCommitReceived(receiveEvent)); } catch (CommitValidationException e) {
} catch (CommitValidationException e) { messages.addAll(e.getMessages());
messages.addAll(e.getMessages()); throw new CommitValidationException(e.getMessage(), messages);
throw new CommitValidationException(e.getMessage(), messages);
}
} }
return messages; return messages;
} }

View File

@@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType; import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.Extension; import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
@@ -40,6 +39,7 @@ import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission; import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@@ -57,7 +57,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
public class MergeValidators { public class MergeValidators {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DynamicSet<MergeValidationListener> mergeValidationListeners; private final PluginSetContext<MergeValidationListener> mergeValidationListeners;
private final ProjectConfigValidator.Factory projectConfigValidatorFactory; private final ProjectConfigValidator.Factory projectConfigValidatorFactory;
private final AccountMergeValidator.Factory accountValidatorFactory; private final AccountMergeValidator.Factory accountValidatorFactory;
private final GroupMergeValidator.Factory groupValidatorFactory; private final GroupMergeValidator.Factory groupValidatorFactory;
@@ -68,7 +68,7 @@ public class MergeValidators {
@Inject @Inject
MergeValidators( MergeValidators(
DynamicSet<MergeValidationListener> mergeValidationListeners, PluginSetContext<MergeValidationListener> mergeValidationListeners,
ProjectConfigValidator.Factory projectConfigValidatorFactory, ProjectConfigValidator.Factory projectConfigValidatorFactory,
AccountMergeValidator.Factory accountValidatorFactory, AccountMergeValidator.Factory accountValidatorFactory,
GroupMergeValidator.Factory groupValidatorFactory) { GroupMergeValidator.Factory groupValidatorFactory) {
@@ -238,10 +238,10 @@ public class MergeValidators {
/** Execute merge validation plug-ins */ /** Execute merge validation plug-ins */
public static class PluginMergeValidationListener implements MergeValidationListener { public static class PluginMergeValidationListener implements MergeValidationListener {
private final DynamicSet<MergeValidationListener> mergeValidationListeners; private final PluginSetContext<MergeValidationListener> mergeValidationListeners;
public PluginMergeValidationListener( public PluginMergeValidationListener(
DynamicSet<MergeValidationListener> mergeValidationListeners) { PluginSetContext<MergeValidationListener> mergeValidationListeners) {
this.mergeValidationListeners = mergeValidationListeners; this.mergeValidationListeners = mergeValidationListeners;
} }
@@ -254,9 +254,9 @@ public class MergeValidators {
PatchSet.Id patchSetId, PatchSet.Id patchSetId,
IdentifiedUser caller) IdentifiedUser caller)
throws MergeValidationException { throws MergeValidationException {
for (MergeValidationListener validator : mergeValidationListeners) { mergeValidationListeners.runEach(
validator.onPreMerge(repo, commit, destProject, destBranch, patchSetId, caller); l -> l.onPreMerge(repo, commit, destProject, destBranch, patchSetId, caller),
} MergeValidationException.class);
} }
} }

View File

@@ -14,9 +14,9 @@
package com.google.gerrit.server.git.validators; package com.google.gerrit.server.git.validators;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.validators.OnSubmitValidationListener.Arguments; import com.google.gerrit.server.git.validators.OnSubmitValidationListener.Arguments;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.submit.IntegrationException; import com.google.gerrit.server.submit.IntegrationException;
import com.google.gerrit.server.update.ChainedReceiveCommands; import com.google.gerrit.server.update.ChainedReceiveCommands;
import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.server.validators.ValidationException;
@@ -29,10 +29,10 @@ public class OnSubmitValidators {
OnSubmitValidators create(); OnSubmitValidators create();
} }
private final DynamicSet<OnSubmitValidationListener> listeners; private final PluginSetContext<OnSubmitValidationListener> listeners;
@Inject @Inject
OnSubmitValidators(DynamicSet<OnSubmitValidationListener> listeners) { OnSubmitValidators(PluginSetContext<OnSubmitValidationListener> listeners) {
this.listeners = listeners; this.listeners = listeners;
} }
@@ -41,11 +41,9 @@ public class OnSubmitValidators {
throws IntegrationException { throws IntegrationException {
try (RevWalk rw = new RevWalk(objectReader)) { try (RevWalk rw = new RevWalk(objectReader)) {
Arguments args = new Arguments(project, rw, commands); Arguments args = new Arguments(project, rw, commands);
for (OnSubmitValidationListener listener : listeners) { listeners.runEach(l -> l.preBranchUpdate(args), ValidationException.class);
listener.preBranchUpdate(args);
}
} catch (ValidationException e) { } catch (ValidationException e) {
throw new IntegrationException(e.getMessage()); throw new IntegrationException(e.getMessage(), e);
} }
} }
} }

View File

@@ -17,7 +17,6 @@ import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
@@ -28,6 +27,7 @@ import com.google.gerrit.server.events.RefReceivedEvent;
import com.google.gerrit.server.permissions.GlobalPermission; import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -52,14 +52,14 @@ public class RefOperationValidators {
private final PermissionBackend.WithUser perm; private final PermissionBackend.WithUser perm;
private final AllUsersName allUsersName; private final AllUsersName allUsersName;
private final DynamicSet<RefOperationValidationListener> refOperationValidationListeners; private final PluginSetContext<RefOperationValidationListener> refOperationValidationListeners;
private final RefReceivedEvent event; private final RefReceivedEvent event;
@Inject @Inject
RefOperationValidators( RefOperationValidators(
PermissionBackend permissionBackend, PermissionBackend permissionBackend,
AllUsersName allUsersName, AllUsersName allUsersName,
DynamicSet<RefOperationValidationListener> refOperationValidationListeners, PluginSetContext<RefOperationValidationListener> refOperationValidationListeners,
@Assisted Project project, @Assisted Project project,
@Assisted IdentifiedUser user, @Assisted IdentifiedUser user,
@Assisted ReceiveCommand cmd) { @Assisted ReceiveCommand cmd) {
@@ -75,13 +75,11 @@ public class RefOperationValidators {
public List<ValidationMessage> validateForRefOperation() throws RefOperationValidationException { public List<ValidationMessage> validateForRefOperation() throws RefOperationValidationException {
List<ValidationMessage> messages = new ArrayList<>(); List<ValidationMessage> messages = new ArrayList<>();
boolean withException = false; boolean withException = false;
List<RefOperationValidationListener> listeners = new ArrayList<>();
listeners.add(new DisallowCreationAndDeletionOfUserBranches(perm, allUsersName));
refOperationValidationListeners.forEach(listeners::add);
try { try {
for (RefOperationValidationListener listener : listeners) { messages.addAll(
messages.addAll(listener.onRefOperation(event)); new DisallowCreationAndDeletionOfUserBranches(perm, allUsersName).onRefOperation(event));
} refOperationValidationListeners.runEach(
l -> l.onRefOperation(event), ValidationException.class);
} catch (ValidationException e) { } catch (ValidationException e) {
messages.add(new ValidationMessage(e.getMessage(), true)); messages.add(new ValidationMessage(e.getMessage(), true));
withException = true; withException = true;

View File

@@ -14,8 +14,8 @@
package com.google.gerrit.server.git.validators; package com.google.gerrit.server.git.validators;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -28,7 +28,7 @@ import org.eclipse.jgit.transport.UploadPack;
public class UploadValidators implements PreUploadHook { public class UploadValidators implements PreUploadHook {
private final DynamicSet<UploadValidationListener> uploadValidationListeners; private final PluginSetContext<UploadValidationListener> uploadValidationListeners;
private final Project project; private final Project project;
private final Repository repository; private final Repository repository;
private final String remoteHost; private final String remoteHost;
@@ -39,7 +39,7 @@ public class UploadValidators implements PreUploadHook {
@Inject @Inject
UploadValidators( UploadValidators(
DynamicSet<UploadValidationListener> uploadValidationListeners, PluginSetContext<UploadValidationListener> uploadValidationListeners,
@Assisted Project project, @Assisted Project project,
@Assisted Repository repository, @Assisted Repository repository,
@Assisted String remoteHost) { @Assisted String remoteHost) {
@@ -53,12 +53,12 @@ public class UploadValidators implements PreUploadHook {
public void onSendPack( public void onSendPack(
UploadPack up, Collection<? extends ObjectId> wants, Collection<? extends ObjectId> haves) UploadPack up, Collection<? extends ObjectId> wants, Collection<? extends ObjectId> haves)
throws ServiceMayNotContinueException { throws ServiceMayNotContinueException {
for (UploadValidationListener validator : uploadValidationListeners) { try {
try { uploadValidationListeners.runEach(
validator.onPreUpload(repository, project, remoteHost, up, wants, haves); l -> l.onPreUpload(repository, project, remoteHost, up, wants, haves),
} catch (ValidationException e) { ValidationException.class);
throw new UploadValidationException(e.getMessage()); } catch (ValidationException e) {
} throw new UploadValidationException(e.getMessage());
} }
} }
@@ -66,12 +66,12 @@ public class UploadValidators implements PreUploadHook {
public void onBeginNegotiateRound( public void onBeginNegotiateRound(
UploadPack up, Collection<? extends ObjectId> wants, int cntOffered) UploadPack up, Collection<? extends ObjectId> wants, int cntOffered)
throws ServiceMayNotContinueException { throws ServiceMayNotContinueException {
for (UploadValidationListener validator : uploadValidationListeners) { try {
try { uploadValidationListeners.runEach(
validator.onBeginNegotiate(repository, project, remoteHost, up, wants, cntOffered); l -> l.onBeginNegotiate(repository, project, remoteHost, up, wants, cntOffered),
} catch (ValidationException e) { ValidationException.class);
throw new UploadValidationException(e.getMessage()); } catch (ValidationException e) {
} throw new UploadValidationException(e.getMessage());
} }
} }

View File

@@ -18,13 +18,13 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.events.AccountIndexedListener; import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index; import com.google.gerrit.index.Index;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.logging.TraceContext; import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer; import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject; import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException; import java.io.IOException;
@@ -42,7 +42,7 @@ public class AccountIndexerImpl implements AccountIndexer {
} }
private final AccountCache byIdCache; private final AccountCache byIdCache;
private final DynamicSet<AccountIndexedListener> indexedListener; private final PluginSetContext<AccountIndexedListener> indexedListener;
private final StalenessChecker stalenessChecker; private final StalenessChecker stalenessChecker;
@Nullable private final AccountIndexCollection indexes; @Nullable private final AccountIndexCollection indexes;
@Nullable private final AccountIndex index; @Nullable private final AccountIndex index;
@@ -50,7 +50,7 @@ public class AccountIndexerImpl implements AccountIndexer {
@AssistedInject @AssistedInject
AccountIndexerImpl( AccountIndexerImpl(
AccountCache byIdCache, AccountCache byIdCache,
DynamicSet<AccountIndexedListener> indexedListener, PluginSetContext<AccountIndexedListener> indexedListener,
StalenessChecker stalenessChecker, StalenessChecker stalenessChecker,
@Assisted AccountIndexCollection indexes) { @Assisted AccountIndexCollection indexes) {
this.byIdCache = byIdCache; this.byIdCache = byIdCache;
@@ -63,7 +63,7 @@ public class AccountIndexerImpl implements AccountIndexer {
@AssistedInject @AssistedInject
AccountIndexerImpl( AccountIndexerImpl(
AccountCache byIdCache, AccountCache byIdCache,
DynamicSet<AccountIndexedListener> indexedListener, PluginSetContext<AccountIndexedListener> indexedListener,
StalenessChecker stalenessChecker, StalenessChecker stalenessChecker,
@Assisted @Nullable AccountIndex index) { @Assisted @Nullable AccountIndex index) {
this.byIdCache = byIdCache; this.byIdCache = byIdCache;
@@ -113,9 +113,7 @@ public class AccountIndexerImpl implements AccountIndexer {
} }
private void fireAccountIndexedEvent(int id) { private void fireAccountIndexedEvent(int id) {
for (AccountIndexedListener listener : indexedListener) { indexedListener.runEach(l -> l.onAccountIndexed(id));
listener.onAccountIndexed(id);
}
} }
private Collection<AccountIndex> getWriteIndexes() { private Collection<AccountIndex> getWriteIndexes() {

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.server.index.change; package com.google.gerrit.server.index.change;
import static com.google.gerrit.server.extensions.events.EventUtil.logEventListenerError;
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH; import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
@@ -24,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.events.ChangeIndexedListener; import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index; import com.google.gerrit.index.Index;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
@@ -37,6 +35,7 @@ import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer; import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NotesMigration; import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.util.RequestContext; import com.google.gerrit.server.util.RequestContext;
@@ -94,7 +93,7 @@ public class ChangeIndexer {
private final ThreadLocalRequestContext context; private final ThreadLocalRequestContext context;
private final ListeningExecutorService batchExecutor; private final ListeningExecutorService batchExecutor;
private final ListeningExecutorService executor; private final ListeningExecutorService executor;
private final DynamicSet<ChangeIndexedListener> indexedListeners; private final PluginSetContext<ChangeIndexedListener> indexedListeners;
private final StalenessChecker stalenessChecker; private final StalenessChecker stalenessChecker;
private final boolean autoReindexIfStale; private final boolean autoReindexIfStale;
@@ -106,7 +105,7 @@ public class ChangeIndexer {
ChangeNotes.Factory changeNotesFactory, ChangeNotes.Factory changeNotesFactory,
ChangeData.Factory changeDataFactory, ChangeData.Factory changeDataFactory,
ThreadLocalRequestContext context, ThreadLocalRequestContext context,
DynamicSet<ChangeIndexedListener> indexedListeners, PluginSetContext<ChangeIndexedListener> indexedListeners,
StalenessChecker stalenessChecker, StalenessChecker stalenessChecker,
@IndexExecutor(BATCH) ListeningExecutorService batchExecutor, @IndexExecutor(BATCH) ListeningExecutorService batchExecutor,
@Assisted ListeningExecutorService executor, @Assisted ListeningExecutorService executor,
@@ -133,7 +132,7 @@ public class ChangeIndexer {
ChangeNotes.Factory changeNotesFactory, ChangeNotes.Factory changeNotesFactory,
ChangeData.Factory changeDataFactory, ChangeData.Factory changeDataFactory,
ThreadLocalRequestContext context, ThreadLocalRequestContext context,
DynamicSet<ChangeIndexedListener> indexedListeners, PluginSetContext<ChangeIndexedListener> indexedListeners,
StalenessChecker stalenessChecker, StalenessChecker stalenessChecker,
@IndexExecutor(BATCH) ListeningExecutorService batchExecutor, @IndexExecutor(BATCH) ListeningExecutorService batchExecutor,
@Assisted ListeningExecutorService executor, @Assisted ListeningExecutorService executor,
@@ -227,23 +226,11 @@ public class ChangeIndexer {
} }
private void fireChangeIndexedEvent(String projectName, int id) { private void fireChangeIndexedEvent(String projectName, int id) {
for (ChangeIndexedListener listener : indexedListeners) { indexedListeners.runEach(l -> l.onChangeIndexed(projectName, id));
try {
listener.onChangeIndexed(projectName, id);
} catch (Exception e) {
logEventListenerError(listener, e);
}
}
} }
private void fireChangeDeletedFromIndexEvent(int id) { private void fireChangeDeletedFromIndexEvent(int id) {
for (ChangeIndexedListener listener : indexedListeners) { indexedListeners.runEach(l -> l.onChangeDeleted(id));
try {
listener.onChangeDeleted(id);
} catch (Exception e) {
logEventListenerError(listener, e);
}
}
} }
/** /**

View File

@@ -18,13 +18,13 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.events.GroupIndexedListener; import com.google.gerrit.extensions.events.GroupIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index; import com.google.gerrit.index.Index;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup; import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.logging.TraceContext; import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer; import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject; import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException; import java.io.IOException;
@@ -42,7 +42,7 @@ public class GroupIndexerImpl implements GroupIndexer {
} }
private final GroupCache groupCache; private final GroupCache groupCache;
private final DynamicSet<GroupIndexedListener> indexedListener; private final PluginSetContext<GroupIndexedListener> indexedListener;
private final StalenessChecker stalenessChecker; private final StalenessChecker stalenessChecker;
@Nullable private final GroupIndexCollection indexes; @Nullable private final GroupIndexCollection indexes;
@Nullable private final GroupIndex index; @Nullable private final GroupIndex index;
@@ -50,7 +50,7 @@ public class GroupIndexerImpl implements GroupIndexer {
@AssistedInject @AssistedInject
GroupIndexerImpl( GroupIndexerImpl(
GroupCache groupCache, GroupCache groupCache,
DynamicSet<GroupIndexedListener> indexedListener, PluginSetContext<GroupIndexedListener> indexedListener,
StalenessChecker stalenessChecker, StalenessChecker stalenessChecker,
@Assisted GroupIndexCollection indexes) { @Assisted GroupIndexCollection indexes) {
this.groupCache = groupCache; this.groupCache = groupCache;
@@ -63,7 +63,7 @@ public class GroupIndexerImpl implements GroupIndexer {
@AssistedInject @AssistedInject
GroupIndexerImpl( GroupIndexerImpl(
GroupCache groupCache, GroupCache groupCache,
DynamicSet<GroupIndexedListener> indexedListener, PluginSetContext<GroupIndexedListener> indexedListener,
StalenessChecker stalenessChecker, StalenessChecker stalenessChecker,
@Assisted @Nullable GroupIndex index) { @Assisted @Nullable GroupIndex index) {
this.groupCache = groupCache; this.groupCache = groupCache;
@@ -113,9 +113,7 @@ public class GroupIndexerImpl implements GroupIndexer {
} }
private void fireGroupIndexedEvent(String uuid) { private void fireGroupIndexedEvent(String uuid) {
for (GroupIndexedListener listener : indexedListener) { indexedListener.runEach(l -> l.onGroupIndexed(uuid));
listener.onGroupIndexed(uuid);
}
} }
private Collection<GroupIndex> getWriteIndexes() { private Collection<GroupIndex> getWriteIndexes() {

View File

@@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.events.ProjectIndexedListener; import com.google.gerrit.extensions.events.ProjectIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.project.ProjectData; import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndex; import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.project.ProjectIndexCollection; import com.google.gerrit.index.project.ProjectIndexCollection;
@@ -26,6 +25,7 @@ import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.logging.TraceContext; import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer; import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -44,14 +44,14 @@ public class ProjectIndexerImpl implements ProjectIndexer {
} }
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final DynamicSet<ProjectIndexedListener> indexedListener; private final PluginSetContext<ProjectIndexedListener> indexedListener;
@Nullable private final ProjectIndexCollection indexes; @Nullable private final ProjectIndexCollection indexes;
@Nullable private final ProjectIndex index; @Nullable private final ProjectIndex index;
@AssistedInject @AssistedInject
ProjectIndexerImpl( ProjectIndexerImpl(
ProjectCache projectCache, ProjectCache projectCache,
DynamicSet<ProjectIndexedListener> indexedListener, PluginSetContext<ProjectIndexedListener> indexedListener,
@Assisted ProjectIndexCollection indexes) { @Assisted ProjectIndexCollection indexes) {
this.projectCache = projectCache; this.projectCache = projectCache;
this.indexedListener = indexedListener; this.indexedListener = indexedListener;
@@ -62,7 +62,7 @@ public class ProjectIndexerImpl implements ProjectIndexer {
@AssistedInject @AssistedInject
ProjectIndexerImpl( ProjectIndexerImpl(
ProjectCache projectCache, ProjectCache projectCache,
DynamicSet<ProjectIndexedListener> indexedListener, PluginSetContext<ProjectIndexedListener> indexedListener,
@Assisted @Nullable ProjectIndex index) { @Assisted @Nullable ProjectIndex index) {
this.projectCache = projectCache; this.projectCache = projectCache;
this.indexedListener = indexedListener; this.indexedListener = indexedListener;
@@ -99,9 +99,7 @@ public class ProjectIndexerImpl implements ProjectIndexer {
} }
private void fireProjectIndexedEvent(String name) { private void fireProjectIndexedEvent(String name) {
for (ProjectIndexedListener listener : indexedListener) { indexedListener.runEach(l -> l.onProjectIndexed(name));
listener.onProjectIndexed(name);
}
} }
private Collection<ProjectIndex> getWriteIndexes() { private Collection<ProjectIndex> getWriteIndexes() {

View File

@@ -94,6 +94,8 @@ import java.util.function.Consumer;
* </pre> * </pre>
*/ */
public class TraceContext implements AutoCloseable { public class TraceContext implements AutoCloseable {
private static final String PLUGIN_TAG = "PLUGIN";
public static TraceContext open() { public static TraceContext open() {
return new TraceContext(); return new TraceContext();
} }
@@ -260,6 +262,10 @@ public class TraceContext implements AutoCloseable {
return this; return this;
} }
public TraceContext addPluginTag(String pluginName) {
return addTag(PLUGIN_TAG, pluginName);
}
public TraceContext forceLogging() { public TraceContext forceLogging() {
if (stopForceLoggingOnClose) { if (stopForceLoggingOnClose) {
return this; return this;

View File

@@ -0,0 +1,330 @@
// Copyright (C) 2018 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.server.plugincontext;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Throwables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.server.logging.TraceContext;
/**
* Context for invoking plugin extensions.
*
* <p>Invoking a plugin extension through a PluginContext sets a logging tag with the plugin name is
* set. This way any errors that are triggered by the plugin extension (even if they happen in
* Gerrit code which is called by the plugin extension) can be easily attributed to the plugin.
*
* <p>If possible plugin extensions should be invoked through:
*
* <ul>
* <li>{@link PluginItemContext} for extensions from {@link DynamicItem}
* <li>{@link PluginSetContext} for extensions from {@link DynamicSet}
* <li>{@link PluginMapContext} for extensions from {@link DynamicMap}
* </ul>
*
* <p>A plugin context can be manually opened by invoking the newTrace methods. This should only be
* needed if an extension throws multiple exceptions that need to be handled:
*
* <pre>
* public interface Foo {
* void doFoo() throws Exception1, Exception2, Exception3;
* }
*
* ...
*
* for (Extension<Foo> fooExtension : fooDynamicMap) {
* try (TraceContext traceContext = PluginContext.newTrace(fooExtension)) {
* fooExtension.get().doFoo();
* }
* }
* </pre>
*
* <p>This class hosts static methods with generic functionality to invoke plugin extensions with a
* trace context that are commonly used by {@link PluginItemContext}, {@link PluginSetContext} and
* {@link PluginMapContext}.
*
* <p>The run* methods execute an extension but don't deliver a result back to the caller.
* Exceptions can be caught and logged.
*
* <p>The call* methods execute an extension and deliver a result back to the caller.
*/
public class PluginContext<T> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@FunctionalInterface
public interface ExtensionImplConsumer<T> {
void run(T t) throws Exception;
}
@FunctionalInterface
public interface ExtensionImplFunction<T, R> {
R call(T input);
}
@FunctionalInterface
public interface CheckedExtensionImplFunction<T, R, X extends Exception> {
R call(T input) throws X;
}
@FunctionalInterface
public interface ExtensionConsumer<T extends Extension<?>> {
void run(T extension) throws Exception;
}
@FunctionalInterface
public interface ExtensionFunction<T extends Extension<?>, R> {
R call(T extension);
}
@FunctionalInterface
public interface CheckedExtensionFunction<T extends Extension<?>, R, X extends Exception> {
R call(T extension) throws X;
}
/**
* Opens a new trace context for invoking a plugin extension.
*
* @param dynamicItem dynamic item that holds the extension implementation that is being invoked
* from within the trace context
* @return the created trace context
*/
public static <T> TraceContext newTrace(DynamicItem<T> dynamicItem) {
Extension<T> extension = dynamicItem.getEntry();
if (extension == null) {
return TraceContext.open();
}
return newTrace(extension);
}
/**
* Opens a new trace context for invoking a plugin extension.
*
* @param extension extension that is being invoked from within the trace context
* @return the created trace context
*/
public static <T> TraceContext newTrace(Extension<T> extension) {
return TraceContext.open().addPluginTag(checkNotNull(extension).getPluginName());
}
/**
* Runs a plugin extension. All exceptions from the plugin extension are caught and logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* @param extension extension that is being invoked
* @param extensionImplConsumer the consumer that invokes the extension
*/
static <T> void runLogExceptions(
Extension<T> extension, ExtensionImplConsumer<T> extensionImplConsumer) {
T extensionImpl = extension.get();
if (extensionImpl == null) {
return;
}
try (TraceContext traceContext = newTrace(extension)) {
extensionImplConsumer.run(extensionImpl);
} catch (Throwable e) {
logger.atWarning().withCause(e).log(
"Failure in %s of plugin %s", extensionImpl.getClass(), extension.getPluginName());
}
}
/**
* Runs a plugin extension. All exceptions from the plugin extension are caught and logged.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extension extension that is being invoked
* @param extensionConsumer the consumer that invokes the extension
*/
static <T> void runLogExceptions(
Extension<T> extension, ExtensionConsumer<Extension<T>> extensionConsumer) {
T extensionImpl = extension.get();
if (extensionImpl == null) {
return;
}
try (TraceContext traceContext = newTrace(extension)) {
extensionConsumer.run(extension);
} catch (Throwable e) {
logger.atWarning().withCause(e).log(
"Failure in %s of plugin %s", extensionImpl.getClass(), extension.getPluginName());
}
}
/**
* Runs a plugin extension. All exceptions from the plugin extension except exceptions of the
* specified type are caught and logged. Exceptions of the specified type are thrown and must be
* handled by the caller.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* @param extension extension that is being invoked
* @param extensionImplConsumer the consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
static <T, X extends Exception> void runLogExceptions(
Extension<T> extension,
ExtensionImplConsumer<T> extensionImplConsumer,
Class<X> exceptionClass)
throws X {
T extensionImpl = extension.get();
if (extensionImpl == null) {
return;
}
try (TraceContext traceContext = newTrace(extension)) {
extensionImplConsumer.run(extensionImpl);
} catch (Throwable e) {
Throwables.throwIfInstanceOf(e, exceptionClass);
Throwables.throwIfUnchecked(e);
logger.atWarning().withCause(e).log(
"Failure in %s of plugin invoke%s", extensionImpl.getClass(), extension.getPluginName());
}
}
/**
* Runs a plugin extension. All exceptions from the plugin extension except exceptions of the
* specified type are caught and logged. Exceptions of the specified type are thrown and must be
* handled by the caller.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extension extension that is being invoked
* @param extensionConsumer the consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
static <T, X extends Exception> void runLogExceptions(
Extension<T> extension,
ExtensionConsumer<Extension<T>> extensionConsumer,
Class<X> exceptionClass)
throws X {
T extensionImpl = extension.get();
if (extensionImpl == null) {
return;
}
try (TraceContext traceContext = newTrace(extension)) {
extensionConsumer.run(extension);
} catch (Throwable e) {
Throwables.throwIfInstanceOf(e, exceptionClass);
Throwables.throwIfUnchecked(e);
logger.atWarning().withCause(e).log(
"Failure in %s of plugin %s", extensionImpl.getClass(), extension.getPluginName());
}
}
/**
* Calls a plugin extension and returns the result from the plugin extension call.
*
* <p>The function gets the extension implementation provided that should be invoked.
*
* @param extension extension that is being invoked
* @param extensionImplFunction function that invokes the extension
* @return the result from the plugin extension
*/
static <T, R> R call(Extension<T> extension, ExtensionImplFunction<T, R> extensionImplFunction) {
try (TraceContext traceContext = newTrace(extension)) {
return extensionImplFunction.call(extension.get());
}
}
/**
* Calls a plugin extension and returns the result from the plugin extension call. Exceptions of
* the specified type are thrown and must be handled by the caller.
*
* <p>The function gets the extension implementation provided that should be invoked.
*
* @param extension extension that is being invoked
* @param checkedExtensionImplFunction function that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @return the result from the plugin extension
* @throws X expected exception from the plugin extension
*/
static <T, R, X extends Exception> R call(
Extension<T> extension,
CheckedExtensionImplFunction<T, R, X> checkedExtensionImplFunction,
Class<X> exceptionClass)
throws X {
try (TraceContext traceContext = newTrace(extension)) {
try {
return checkedExtensionImplFunction.call(extension.get());
} catch (Exception e) {
// The only exception that can be thrown is X, but we cannot catch X since it is a generic
// type.
Throwables.throwIfInstanceOf(e, exceptionClass);
Throwables.throwIfUnchecked(e);
throw new IllegalStateException("unexpected exception: " + e.getMessage(), e);
}
}
}
/**
* Calls a plugin extension and returns the result from the plugin extension call.
*
* <p>The function get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extension extension that is being invoked
* @param extensionFunction function that invokes the extension
* @return the result from the plugin extension
*/
static <T, R> R call(
Extension<T> extension, ExtensionFunction<Extension<T>, R> extensionFunction) {
try (TraceContext traceContext = newTrace(extension)) {
return extensionFunction.call(extension);
}
}
/**
* Calls a plugin extension and returns the result from the plugin extension call. Exceptions of
* the specified type are thrown and must be handled by the caller.
*
* <p>The function get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extension extension that is being invoked
* @param checkedExtensionFunction function that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @return the result from the plugin extension
* @throws X expected exception from the plugin extension
*/
static <T, R, X extends Exception> R call(
Extension<T> extension,
CheckedExtensionFunction<Extension<T>, R, X> checkedExtensionFunction,
Class<X> exceptionClass)
throws X {
try (TraceContext traceContext = newTrace(extension)) {
try {
return checkedExtensionFunction.call(extension);
} catch (Exception e) {
// The only exception that can be thrown is X, but we cannot catch X since it is a generic
// type.
Throwables.throwIfInstanceOf(e, exceptionClass);
Throwables.throwIfUnchecked(e);
throw new IllegalStateException("unexpected exception: " + e.getMessage(), e);
}
}
}
}

View File

@@ -0,0 +1,180 @@
// Copyright (C) 2018 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.server.plugincontext;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.server.plugincontext.PluginContext.CheckedExtensionImplFunction;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplConsumer;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplFunction;
import com.google.inject.Inject;
/**
* Context to invoke an extension from a {@link DynamicItem}.
*
* <p>When the plugin extension is invoked a logging tag with the plugin name is set. This way any
* errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
* called by the plugin extension) can be easily attributed to the plugin.
*
* <p>The run* methods execute an extension but don't deliver a result back to the caller.
* Exceptions can be caught and logged.
*
* <p>The call* methods execute an extension and deliver a result back to the caller.
*
* <p>Example if all exceptions should be caught and logged:
*
* <pre>
* fooPluginItemContext.run(foo -> foo.doFoo());
* </pre>
*
* <p>Example if all exceptions, but one, should be caught and logged:
*
* <pre>
* try {
* fooPluginItemContext.run(foo -> foo.doFoo(), MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if return values should be handled:
*
* <pre>
* Object result = fooPluginItemContext.call(foo -> foo.getFoo());
* </pre>
*
* <p>Example if return values and a single exception should be handled:
*
* <pre>
* Object result;
* try {
* result = fooPluginItemContext.call(foo -> foo.getFoo(), MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if several exceptions should be handled:
*
* <pre>
* try (TraceContext traceContext = PluginContext.newTrace(fooDynamicItem.getEntry())) {
* fooDynamicItem.get().doFoo();
* } catch (MyException1 | MyException2 | MyException3 e) {
* // handle the exception
* }
* </pre>
*/
public class PluginItemContext<T> {
@Nullable private final DynamicItem<T> dynamicItem;
@VisibleForTesting
@Inject
public PluginItemContext(DynamicItem<T> dynamicItem) {
this.dynamicItem = dynamicItem;
}
/**
* Returns the name of the plugin that registered the extension.
*
* @return the plugin name, {@code null} if no implementation is registered for this extension
* point
*/
@Nullable
public String getPluginName() {
return dynamicItem.getPluginName();
}
/**
* Invokes the plugin extension of the item. All exceptions from the plugin extension are caught
* and logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* <p>No-op if no implementation is registered for this extension point.
*
* @param extensionImplConsumer consumer that invokes the extension
*/
public void run(ExtensionImplConsumer<T> extensionImplConsumer) {
Extension<T> extension = dynamicItem.getEntry();
if (extension == null) {
return;
}
PluginContext.runLogExceptions(extension, extensionImplConsumer);
}
/**
* Invokes the plugin extension of the item. All exceptions from the plugin extension are caught
* and logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* <p>No-op if no implementation is registered for this extension point.
*
* @param extensionImplConsumer consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
public <X extends Exception> void run(
ExtensionImplConsumer<T> extensionImplConsumer, Class<X> exceptionClass) throws X {
Extension<T> extension = dynamicItem.getEntry();
if (extension == null) {
return;
}
PluginContext.runLogExceptions(extension, extensionImplConsumer, exceptionClass);
}
/**
* Calls the plugin extension of the item and returns the result from the plugin extension call.
*
* <p>The function gets the extension implementation provided that should be invoked.
*
* <p>Fails with {@link IllegalStateException} if no implementation is registered for the item.
*
* @param extensionImplFunction function that invokes the extension
* @return the result from the plugin extension
* @throws IllegalStateException if no implementation is registered for the item
*/
public <R> R call(ExtensionImplFunction<T, R> extensionImplFunction) {
Extension<T> extension = dynamicItem.getEntry();
checkState(extension != null);
return PluginContext.call(extension, extensionImplFunction);
}
/**
* Calls the plugin extension of the item and returns the result from the plugin extension call.
* Exceptions of the specified type are thrown and must be handled by the caller.
*
* <p>The function gets the extension implementation provided that should be invoked.
*
* <p>Fails with {@link IllegalStateException} if no implementation is registered for the item.
*
* @param checkedExtensionImplFunction function that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @return the result from the plugin extension
* @throws X expected exception from the plugin extension
* @throws IllegalStateException if no implementation is registered for the item
*/
public <R, X extends Exception> R call(
CheckedExtensionImplFunction<T, R, X> checkedExtensionImplFunction, Class<X> exceptionClass)
throws X {
Extension<T> extension = dynamicItem.getEntry();
checkState(extension != null);
return PluginContext.call(extension, checkedExtensionImplFunction, exceptionClass);
}
}

View File

@@ -0,0 +1,172 @@
// Copyright (C) 2018 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.server.plugincontext;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterators;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionConsumer;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.SortedSet;
/**
* Context to invoke extensions from a {@link DynamicMap}.
*
* <p>When a plugin extension is invoked a logging tag with the plugin name is set. This way any
* errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
* called by the plugin extension) can be easily attributed to the plugin.
*
* <p>Example if all exceptions should be caught and logged:
*
* <pre>
* Map<String, Object> results = new HashMap<>();
* fooPluginMapContext.runEach(
* extension -> results.put(extension.getExportName(), extension.get().getFoo());
* </pre>
*
* <p>Example if all exceptions, but one, should be caught and logged:
*
* <pre>
* Map<String, Object> results = new HashMap<>();
* try {
* fooPluginMapContext.runEach(
* extension -> results.put(extension.getExportName(), extension.get().getFoo(),
* MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if return values should be handled:
*
* <pre>
* Map<String, Object> results = new HashMap<>();
* for (PluginMapEntryContext<Foo> c : fooPluginMapContext) {
* if (c.call(extension -> extension.get().handles(x))) {
* c.run(extension -> results.put(extension.getExportName(), extension.get().getFoo());
* }
* }
* </pre>
*
* <p>Example if return values and a single exception should be handled:
*
* <pre>
* Map<String, Object> results = new HashMap<>();
* try {
* for (PluginMapEntryContext<Foo> c : fooPluginMapContext) {
* if (c.call(extension -> extension.handles(x), MyException.class)) {
* c.run(extension -> results.put(extension.getExportName(), extension.get().getFoo(),
* MyException.class);
* }
* }
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if several exceptions should be handled:
*
* <pre>
* for (Extension<Foo> fooExtension : fooDynamicMap) {
* try (TraceContext traceContext = PluginContext.newTrace(fooExtension)) {
* fooExtension.get().doFoo();
* } catch (MyException1 | MyException2 | MyException3 e) {
* // handle the exception
* }
* }
* </pre>
*/
public class PluginMapContext<T> implements Iterable<PluginMapEntryContext<T>> {
private final DynamicMap<T> dynamicMap;
@VisibleForTesting
@Inject
public PluginMapContext(DynamicMap<T> dynamicMap) {
this.dynamicMap = dynamicMap;
}
/**
* Iterator that provides contexts for invoking the extensions in this map.
*
* <p>This is useful if:
*
* <ul>
* <li>invoking of each extension returns a result that should be handled
* <li>a sequence of invocations should be done on each extension
* </ul>
*/
@Override
public Iterator<PluginMapEntryContext<T>> iterator() {
return Iterators.transform(dynamicMap.iterator(), PluginMapEntryContext<T>::new);
}
/**
* Checks if no implementations for this extension point have been registered.
*
* @return {@code true} if no implementations for this extension point have been registered,
* otherwise {@code false}
*/
public boolean isEmpty() {
return !dynamicMap.iterator().hasNext();
}
/**
* Returns a sorted list of the plugins that have registered implementations for this extension
* point.
*
* @return sorted list of the plugins that have registered implementations for this extension
* point
*/
public SortedSet<String> plugins() {
return dynamicMap.plugins();
}
/**
* Invokes each extension in the map. All exceptions from the plugin extensions are caught and
* logged.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* <p>All extension in the map are invoked, even if invoking some of the extensions failed.
*
* @param extensionConsumer consumer that invokes the extension
*/
public void runEach(ExtensionConsumer<Extension<T>> extensionConsumer) {
dynamicMap.forEach(p -> PluginContext.runLogExceptions(p, extensionConsumer));
}
/**
* Invokes each extension in the map. All exceptions from the plugin extensions except exceptions
* of the specified type are caught and logged.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* <p>All extension in the map are invoked, even if invoking some of the extensions failed.
*
* @param extensionConsumer consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
public <X extends Exception> void runEach(
ExtensionConsumer<Extension<T>> extensionConsumer, Class<X> exceptionClass) throws X {
for (Extension<T> extension : dynamicMap) {
PluginContext.runLogExceptions(extension, extensionConsumer, exceptionClass);
}
}
}

View File

@@ -0,0 +1,170 @@
// Copyright (C) 2018 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.server.plugincontext;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.server.plugincontext.PluginContext.CheckedExtensionFunction;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionConsumer;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionFunction;
/**
* Context to invoke an extension from {@link DynamicMap}.
*
* <p>When the plugin extension is invoked a logging tag with the plugin name is set. This way any
* errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
* called by the plugin extension) can be easily attributed to the plugin.
*
* <p>The run* methods execute the extension but don't deliver a result back to the caller.
* Exceptions can be caught and logged.
*
* <p>The call* methods execute the extension and deliver a result back to the caller.
*
* <pre>
* Map<String, Object> results = new HashMap<>();
* fooPluginMapEntryContext.run(
* extension -> results.put(extension.getExportName(), extension.get().getFoo());
* </pre>
*
* <p>Example if all exceptions, but one, should be caught and logged:
*
* <pre>
* Map<String, Object> results = new HashMap<>();
* try {
* fooPluginMapEntryContext.run(
* extension -> results.put(extension.getExportName(), extension.get().getFoo(),
* MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if return values should be handled:
*
* <pre>
* Object result = fooPluginMapEntryContext.call(extension -> extension.get().getFoo());
* </pre>
*
* <p>Example if return values and a single exception should be handled:
*
* <pre>
* Object result;
* try {
* result = fooPluginMapEntryContext.call(extension -> extension.get().getFoo(), MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if several exceptions should be handled:
*
* <pre>
* for (Extension<Foo> fooExtension : fooDynamicMap) {
* try (TraceContext traceContext = PluginContext.newTrace(fooExtension)) {
* fooExtension.get().doFoo();
* } catch (MyException1 | MyException2 | MyException3 e) {
* // handle the exception
* }
* }
* </pre>
*/
public class PluginMapEntryContext<T> {
private final Extension<T> extension;
PluginMapEntryContext(Extension<T> extension) {
checkNotNull(extension);
checkNotNull(extension.getExportName(), "export name must be set for plugin map entries");
this.extension = extension;
}
/**
* Returns the name of the plugin that registered this map entry.
*
* @return the plugin name
*/
public String getPluginName() {
return extension.getPluginName();
}
/**
* Returns the export name for which this map entry was registered.
*
* @return the export name
*/
public String getExportName() {
return extension.getExportName();
}
/**
* Invokes the plugin extension. All exceptions from the plugin extension are caught and logged.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extensionConsumer consumer that invokes the extension
*/
public void run(ExtensionConsumer<Extension<T>> extensionConsumer) {
PluginContext.runLogExceptions(extension, extensionConsumer);
}
/**
* Invokes the plugin extension. All exceptions from the plugin extension are caught and logged.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extensionConsumer consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
public <X extends Exception> void run(
ExtensionConsumer<Extension<T>> extensionConsumer, Class<X> exceptionClass) throws X {
PluginContext.runLogExceptions(extension, extensionConsumer, exceptionClass);
}
/**
* Calls the plugin extension and returns the result from the plugin extension call.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param extensionFunction function that invokes the extension
* @return the result from the plugin extension
*/
public <R> R call(ExtensionFunction<Extension<T>, R> extensionFunction) {
return PluginContext.call(extension, extensionFunction);
}
/**
* Calls the plugin extension and returns the result from the plugin extension call. Exceptions of
* the specified type are thrown and must be handled by the caller.
*
* <p>The consumer get the {@link Extension} provided that should be invoked. The extension
* provides access to the plugin name and the export name.
*
* @param checkedExtensionFunction function that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @return the result from the plugin extension
* @throws X expected exception from the plugin extension
*/
public <R, X extends Exception> R call(
CheckedExtensionFunction<Extension<T>, R, X> checkedExtensionFunction,
Class<X> exceptionClass)
throws X {
return PluginContext.call(extension, checkedExtensionFunction, exceptionClass);
}
}

View File

@@ -0,0 +1,162 @@
// Copyright (C) 2018 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.server.plugincontext;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterators;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplConsumer;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.SortedSet;
/**
* Context to invoke extensions from a {@link DynamicSet}.
*
* <p>When a plugin extension is invoked a logging tag with the plugin name is set. This way any
* errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
* called by the plugin extension) can be easily attributed to the plugin.
*
* <p>Example if all exceptions should be caught and logged:
*
* <pre>
* fooPluginSetContext.runEach(foo -> foo.doFoo());
* </pre>
*
* <p>Example if all exceptions, but one, should be caught and logged:
*
* <pre>
* try {
* fooPluginSetContext.runEach(foo -> foo.doFoo(), MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if return values should be handled:
*
* <pre>
* for (PluginSetEntryContext<Foo> c : fooPluginSetContext) {
* if (c.call(foo -> foo.handles(x))) {
* c.run(foo -> foo.doFoo());
* }
* }
* </pre>
*
* <p>Example if return values and a single exception should be handled:
*
* <pre>
* try {
* for (PluginSetEntryContext<Foo> c : fooPluginSetContext) {
* if (c.call(foo -> foo.handles(x), MyException.class)) {
* c.run(foo -> foo.doFoo(), MyException.class);
* }
* }
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if several exceptions should be handled:
*
* <pre>
* for (Extension<Foo> fooExtension : fooDynamicSet.entries()) {
* try (TraceContext traceContext = PluginContext.newTrace(fooExtension)) {
* fooExtension.get().doFoo();
* } catch (MyException1 | MyException2 | MyException3 e) {
* // handle the exception
* }
* }
* </pre>
*/
public class PluginSetContext<T> implements Iterable<PluginSetEntryContext<T>> {
private final DynamicSet<T> dynamicSet;
@VisibleForTesting
@Inject
public PluginSetContext(DynamicSet<T> dynamicSet) {
this.dynamicSet = dynamicSet;
}
/**
* Iterator that provides contexts for invoking the extensions in this set.
*
* <p>This is useful if:
*
* <ul>
* <li>invoking of each extension returns a result that should be handled
* <li>a sequence of invocations should be done on each extension
* </ul>
*/
@Override
public Iterator<PluginSetEntryContext<T>> iterator() {
return Iterators.transform(dynamicSet.entries().iterator(), PluginSetEntryContext<T>::new);
}
/**
* Checks if no implementations for this extension point have been registered.
*
* @return {@code true} if no implementations for this extension point have been registered,
* otherwise {@code false}
*/
public boolean isEmpty() {
return !dynamicSet.iterator().hasNext();
}
/**
* Returns a sorted list of the plugins that have registered implementations for this extension
* point.
*
* @return sorted list of the plugins that have registered implementations for this extension
* point
*/
public SortedSet<String> plugins() {
return dynamicSet.plugins();
}
/**
* Invokes each extension in the set. All exceptions from the plugin extensions are caught and
* logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* <p>All extension in the set are invoked, even if invoking some of the extensions failed.
*
* @param extensionImplConsumer consumer that invokes the extension
*/
public void runEach(ExtensionImplConsumer<T> extensionImplConsumer) {
dynamicSet.entries().forEach(p -> PluginContext.runLogExceptions(p, extensionImplConsumer));
}
/**
* Invokes each extension in the set. All exceptions from the plugin extensions except exceptions
* of the specified type are caught and logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* <p>All extension in the set are invoked, even if invoking some of the extensions failed.
*
* @param extensionImplConsumer consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
public <X extends Exception> void runEach(
ExtensionImplConsumer<T> extensionImplConsumer, Class<X> exceptionClass) throws X {
for (Extension<T> extension : dynamicSet.entries()) {
PluginContext.runLogExceptions(extension, extensionImplConsumer, exceptionClass);
}
}
}

View File

@@ -0,0 +1,165 @@
// Copyright (C) 2018 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.server.plugincontext;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.server.plugincontext.PluginContext.CheckedExtensionImplFunction;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplConsumer;
import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplFunction;
/**
* Context to invoke an extension from {@link DynamicSet}.
*
* <p>When the plugin extension is invoked a logging tag with the plugin name is set. This way any
* errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
* called by the plugin extension) can be easily attributed to the plugin.
*
* <p>The run* methods execute the extension but don't deliver a result back to the caller.
* Exceptions can be caught and logged.
*
* <p>The call* methods execute the extension and deliver a result back to the caller.
*
* <p>Example if all exceptions should be caught and logged:
*
* <pre>
* fooPluginSetEntryContext.run(foo -> foo.doFoo());
* </pre>
*
* <p>Example if all exceptions, but one, should be caught and logged:
*
* <pre>
* try {
* fooPluginSetEntryContext.run(foo -> foo.doFoo(), MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if return values should be handled:
*
* <pre>
* Object result = fooPluginSetEntryContext.call(foo -> foo.getFoo());
* </pre>
*
* <p>Example if return values and a single exception should be handled:
*
* <pre>
* Object result;
* try {
* result = fooPluginSetEntryContext.call(foo -> foo.getFoo(), MyException.class);
* } catch (MyException e) {
* // handle the exception
* }
* </pre>
*
* <p>Example if several exceptions should be handled:
*
* <pre>
* for (Extension<Foo> fooExtension : fooDynamicSet.entries()) {
* try (TraceContext traceContext = PluginContext.newTrace(fooExtension)) {
* fooExtension.get().doFoo();
* } catch (MyException1 | MyException2 | MyException3 e) {
* // handle the exception
* }
* }
* </pre>
*/
public class PluginSetEntryContext<T> {
private final Extension<T> extension;
PluginSetEntryContext(Extension<T> extension) {
this.extension = checkNotNull(extension);
}
/**
* Returns the name of the plugin that registered this extension.
*
* @return the plugin name
*/
public String getPluginName() {
return extension.getPluginName();
}
/**
* Returns the implementation of this extension.
*
* <p>Should only be used in exceptional cases to get direct access to the extension
* implementation. If possible the extension should be invoked through {@link
* #run(ExtensionImplConsumer)}, {@link #run(ExtensionImplConsumer, Class)}, {@link
* #call(ExtensionImplFunction)} and {@link #call(CheckedExtensionImplFunction, Class)}.
*
* @return the implementation of this extension
*/
public T get() {
return extension.get();
}
/**
* Invokes the plugin extension. All exceptions from the plugin extension are caught and logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* @param extensionImplConsumer consumer that invokes the extension
*/
public void run(ExtensionImplConsumer<T> extensionImplConsumer) {
PluginContext.runLogExceptions(extension, extensionImplConsumer);
}
/**
* Invokes the plugin extension. All exceptions from the plugin extension are caught and logged.
*
* <p>The consumer gets the extension implementation provided that should be invoked.
*
* @param extensionImplConsumer consumer that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @throws X expected exception from the plugin extension
*/
public <X extends Exception> void run(
ExtensionImplConsumer<T> extensionImplConsumer, Class<X> exceptionClass) throws X {
PluginContext.runLogExceptions(extension, extensionImplConsumer, exceptionClass);
}
/**
* Calls the plugin extension and returns the result from the plugin extension call.
*
* <p>The function gets the extension point provided that should be invoked.
*
* @param extensionImplFunction function that invokes the extension
* @return the result from the plugin extension
*/
public <R> R call(ExtensionImplFunction<T, R> extensionImplFunction) {
return PluginContext.call(extension, extensionImplFunction);
}
/**
* Calls the plugin extension and returns the result from the plugin extension call. Exceptions of
* the specified type are thrown and must be handled by the caller.
*
* <p>The function gets the extension implementation provided that should be invoked.
*
* @param checkedExtensionImplFunction function that invokes the extension
* @param exceptionClass type of the exceptions that should be thrown
* @return the result from the plugin extension
* @throws X expected exception from the plugin extension
*/
public <R, X extends Exception> R call(
CheckedExtensionImplFunction<T, R, X> checkedExtensionImplFunction, Class<X> exceptionClass)
throws X {
return PluginContext.call(extension, checkedExtensionImplFunction, exceptionClass);
}
}

View File

@@ -14,11 +14,12 @@
package com.google.gerrit.server.project; package com.google.gerrit.server.project;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord; import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.PrologRule; import com.google.gerrit.server.rules.PrologRule;
import com.google.gerrit.server.rules.SubmitRule; import com.google.gerrit.server.rules.SubmitRule;
@@ -29,7 +30,6 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/** /**
* Evaluates a submit-like Prolog rule found in the rules.pl file of the current project and filters * Evaluates a submit-like Prolog rule found in the rules.pl file of the current project and filters
@@ -42,7 +42,7 @@ public class SubmitRuleEvaluator {
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final PrologRule prologRule; private final PrologRule prologRule;
private final DynamicSet<SubmitRule> submitRules; private final PluginSetContext<SubmitRule> submitRules;
private final SubmitRuleOptions opts; private final SubmitRuleOptions opts;
public interface Factory { public interface Factory {
@@ -54,7 +54,7 @@ public class SubmitRuleEvaluator {
private SubmitRuleEvaluator( private SubmitRuleEvaluator(
ProjectCache projectCache, ProjectCache projectCache,
PrologRule prologRule, PrologRule prologRule,
DynamicSet<SubmitRule> submitRules, PluginSetContext<SubmitRule> submitRules,
@Assisted SubmitRuleOptions options) { @Assisted SubmitRuleOptions options) {
this.projectCache = projectCache; this.projectCache = projectCache;
this.prologRule = prologRule; this.prologRule = prologRule;
@@ -110,8 +110,8 @@ public class SubmitRuleEvaluator {
// We evaluate all the plugin-defined evaluators, // We evaluate all the plugin-defined evaluators,
// and then we collect the results in one list. // and then we collect the results in one list.
return StreamSupport.stream(submitRules.spliterator(), false) return Streams.stream(submitRules)
.map(s -> s.evaluate(cd, opts)) .map(c -> c.call(s -> s.evaluate(cd, opts)))
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@@ -18,6 +18,7 @@ java_library(
"//java/com/google/gerrit/reviewdb:server", "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server", "//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/ioutil", "//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/time", "//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli", "//java/com/google/gerrit/util/cli",
"//java/org/eclipse/jgit:server", "//java/org/eclipse/jgit:server",

View File

@@ -16,7 +16,6 @@ package com.google.gerrit.server.restapi.change;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
@@ -27,6 +26,7 @@ import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.StarredChangesUtil; import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.change.AccountPatchReviewStore; import com.google.gerrit.server.change.AccountPatchReviewStore;
import com.google.gerrit.server.extensions.events.ChangeDeleted; import com.google.gerrit.server.extensions.events.ChangeDeleted;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.BatchUpdateReviewDb; import com.google.gerrit.server.update.BatchUpdateReviewDb;
@@ -45,7 +45,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
class DeleteChangeOp implements BatchUpdateOp { class DeleteChangeOp implements BatchUpdateOp {
private final PatchSetUtil psUtil; private final PatchSetUtil psUtil;
private final StarredChangesUtil starredChangesUtil; private final StarredChangesUtil starredChangesUtil;
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore; private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
private final ChangeDeleted changeDeleted; private final ChangeDeleted changeDeleted;
private Change.Id id; private Change.Id id;
@@ -54,7 +54,7 @@ class DeleteChangeOp implements BatchUpdateOp {
DeleteChangeOp( DeleteChangeOp(
PatchSetUtil psUtil, PatchSetUtil psUtil,
StarredChangesUtil starredChangesUtil, StarredChangesUtil starredChangesUtil,
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore, PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
ChangeDeleted changeDeleted) { ChangeDeleted changeDeleted) {
this.psUtil = psUtil; this.psUtil = psUtil;
this.starredChangesUtil = starredChangesUtil; this.starredChangesUtil = starredChangesUtil;
@@ -127,7 +127,7 @@ class DeleteChangeOp implements BatchUpdateOp {
private void cleanUpReferences(ChangeContext ctx, Change.Id id, Collection<PatchSet> patchSets) private void cleanUpReferences(ChangeContext ctx, Change.Id id, Collection<PatchSet> patchSets)
throws OrmException, NoSuchChangeException { throws OrmException, NoSuchChangeException {
for (PatchSet ps : patchSets) { for (PatchSet ps : patchSets) {
accountPatchReviewStore.get().clearReviewed(ps.getId()); accountPatchReviewStore.run(s -> s.clearReviewed(ps.getId()), OrmException.class);
} }
// Non-atomic operation on Accounts table; not much we can do to make it // Non-atomic operation on Accounts table; not much we can do to make it

View File

@@ -19,7 +19,6 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hasher; import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.gerrit.extensions.common.FileInfo; import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -49,6 +48,7 @@ import com.google.gerrit.server.patch.PatchListKey;
import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
@@ -120,7 +120,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
private final GitRepositoryManager gitManager; private final GitRepositoryManager gitManager;
private final PatchListCache patchListCache; private final PatchListCache patchListCache;
private final PatchSetUtil psUtil; private final PatchSetUtil psUtil;
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore; private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
@Inject @Inject
ListFiles( ListFiles(
@@ -131,7 +131,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
GitRepositoryManager gitManager, GitRepositoryManager gitManager,
PatchListCache patchListCache, PatchListCache patchListCache,
PatchSetUtil psUtil, PatchSetUtil psUtil,
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore) { PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
this.db = db; this.db = db;
this.self = self; this.self = self;
this.fileInfoJson = fileInfoJson; this.fileInfoJson = fileInfoJson;
@@ -235,8 +235,10 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
Account.Id userId = user.getAccountId(); Account.Id userId = user.getAccountId();
PatchSet patchSetId = resource.getPatchSet(); PatchSet patchSetId = resource.getPatchSet();
Optional<PatchSetWithReviewedFiles> o = Optional<PatchSetWithReviewedFiles> o;
accountPatchReviewStore.get().findReviewed(patchSetId.getId(), userId); o =
accountPatchReviewStore.call(
s -> s.findReviewed(patchSetId.getId(), userId), OrmException.class);
if (o.isPresent()) { if (o.isPresent()) {
PatchSetWithReviewedFiles res = o.get(); PatchSetWithReviewedFiles res = o.get();
@@ -317,9 +319,10 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
pathList.add(path); pathList.add(path);
} }
} }
accountPatchReviewStore
.get() accountPatchReviewStore.run(
.markReviewed(resource.getPatchSet().getId(), userId, pathList); s -> s.markReviewed(resource.getPatchSet().getId(), userId, pathList),
OrmException.class);
return pathList; return pathList;
} }
} }

View File

@@ -15,11 +15,11 @@
package com.google.gerrit.server.restapi.change; package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.Input; import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.change.AccountPatchReviewStore; import com.google.gerrit.server.change.AccountPatchReviewStore;
import com.google.gerrit.server.change.FileResource; import com.google.gerrit.server.change.FileResource;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -28,44 +28,45 @@ public class Reviewed {
@Singleton @Singleton
public static class PutReviewed implements RestModifyView<FileResource, Input> { public static class PutReviewed implements RestModifyView<FileResource, Input> {
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore; private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
@Inject @Inject
PutReviewed(DynamicItem<AccountPatchReviewStore> accountPatchReviewStore) { PutReviewed(PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
this.accountPatchReviewStore = accountPatchReviewStore; this.accountPatchReviewStore = accountPatchReviewStore;
} }
@Override @Override
public Response<String> apply(FileResource resource, Input input) throws OrmException { public Response<String> apply(FileResource resource, Input input) throws OrmException {
if (accountPatchReviewStore boolean reviewFlagUpdated =
.get() accountPatchReviewStore.call(
.markReviewed( s ->
resource.getPatchKey().getParentKey(), s.markReviewed(
resource.getAccountId(), resource.getPatchKey().getParentKey(),
resource.getPatchKey().getFileName())) { resource.getAccountId(),
return Response.created(""); resource.getPatchKey().getFileName()),
} OrmException.class);
return Response.ok(""); return reviewFlagUpdated ? Response.created("") : Response.ok("");
} }
} }
@Singleton @Singleton
public static class DeleteReviewed implements RestModifyView<FileResource, Input> { public static class DeleteReviewed implements RestModifyView<FileResource, Input> {
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore; private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
@Inject @Inject
DeleteReviewed(DynamicItem<AccountPatchReviewStore> accountPatchReviewStore) { DeleteReviewed(PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
this.accountPatchReviewStore = accountPatchReviewStore; this.accountPatchReviewStore = accountPatchReviewStore;
} }
@Override @Override
public Response<?> apply(FileResource resource, Input input) throws OrmException { public Response<?> apply(FileResource resource, Input input) throws OrmException {
accountPatchReviewStore accountPatchReviewStore.run(
.get() s ->
.clearReviewed( s.clearReviewed(
resource.getPatchKey().getParentKey(), resource.getPatchKey().getParentKey(),
resource.getAccountId(), resource.getAccountId(),
resource.getPatchKey().getFileName()); resource.getPatchKey().getFileName()),
OrmException.class);
return Response.none(); return Response.none();
} }
} }

View File

@@ -23,8 +23,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.index.query.Predicate; import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException; import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
@@ -37,6 +35,7 @@ import com.google.gerrit.server.change.SuggestedReviewer;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.change.ChangeField; import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.plugincontext.PluginMapContext;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder; import com.google.gerrit.server.query.change.ChangeQueryBuilder;
@@ -78,7 +77,7 @@ public class ReviewerRecommender {
private final ChangeQueryBuilder changeQueryBuilder; private final ChangeQueryBuilder changeQueryBuilder;
private final Config config; private final Config config;
private final DynamicMap<ReviewerSuggestion> reviewerSuggestionPluginMap; private final PluginMapContext<ReviewerSuggestion> reviewerSuggestionPluginMap;
private final Provider<InternalChangeQuery> queryProvider; private final Provider<InternalChangeQuery> queryProvider;
private final ExecutorService executor; private final ExecutorService executor;
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;
@@ -87,7 +86,7 @@ public class ReviewerRecommender {
@Inject @Inject
ReviewerRecommender( ReviewerRecommender(
ChangeQueryBuilder changeQueryBuilder, ChangeQueryBuilder changeQueryBuilder,
DynamicMap<ReviewerSuggestion> reviewerSuggestionPluginMap, PluginMapContext<ReviewerSuggestion> reviewerSuggestionPluginMap,
Provider<InternalChangeQuery> queryProvider, Provider<InternalChangeQuery> queryProvider,
@FanOutExecutor ExecutorService executor, @FanOutExecutor ExecutorService executor,
Provider<ReviewDb> dbProvider, Provider<ReviewDb> dbProvider,
@@ -131,30 +130,30 @@ public class ReviewerRecommender {
new ArrayList<>(reviewerSuggestionPluginMap.plugins().size()); new ArrayList<>(reviewerSuggestionPluginMap.plugins().size());
List<Double> weights = new ArrayList<>(reviewerSuggestionPluginMap.plugins().size()); List<Double> weights = new ArrayList<>(reviewerSuggestionPluginMap.plugins().size());
for (Extension<ReviewerSuggestion> plugin : reviewerSuggestionPluginMap) { reviewerSuggestionPluginMap.runEach(
tasks.add( extension -> {
() -> tasks.add(
plugin () ->
.getProvider() extension
.get() .get()
.suggestReviewers( .suggestReviewers(
projectState.getNameKey(), projectState.getNameKey(),
changeNotes != null ? changeNotes.getChangeId() : null, changeNotes != null ? changeNotes.getChangeId() : null,
query, query,
reviewerScores.keySet())); reviewerScores.keySet()));
String key = plugin.getPluginName() + "-" + plugin.getExportName(); String key = extension.getPluginName() + "-" + extension.getExportName();
String pluginWeight = config.getString("addReviewer", key, "weight"); String pluginWeight = config.getString("addReviewer", key, "weight");
if (Strings.isNullOrEmpty(pluginWeight)) { if (Strings.isNullOrEmpty(pluginWeight)) {
pluginWeight = "1"; pluginWeight = "1";
} }
logger.atFine().log("weight for %s: %s", key, pluginWeight); logger.atFine().log("weight for %s: %s", key, pluginWeight);
try { try {
weights.add(Double.parseDouble(pluginWeight)); weights.add(Double.parseDouble(pluginWeight));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.atSevere().withCause(e).log("Exception while parsing weight for %s", key); logger.atSevere().withCause(e).log("Exception while parsing weight for %s", key);
weights.add(1d); weights.add(1d);
} }
} });
try { try {
List<Future<Set<SuggestedReviewer>>> futures = List<Future<Set<SuggestedReviewer>>> futures =

View File

@@ -23,7 +23,6 @@ import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.groups.GroupInput; import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.client.ListGroupsOption; import com.google.gerrit.extensions.client.ListGroupsOption;
import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.IdString;
@@ -51,6 +50,7 @@ import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupCreation; import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.group.db.InternalGroupUpdate; import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.GroupCreationValidationListener; import com.google.gerrit.server.validators.GroupCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.server.validators.ValidationException;
import com.google.gwtorm.server.OrmDuplicateKeyException; import com.google.gwtorm.server.OrmDuplicateKeyException;
@@ -79,7 +79,7 @@ public class CreateGroup
private final GroupCache groupCache; private final GroupCache groupCache;
private final GroupsCollection groups; private final GroupsCollection groups;
private final GroupJson json; private final GroupJson json;
private final DynamicSet<GroupCreationValidationListener> groupCreationValidationListeners; private final PluginSetContext<GroupCreationValidationListener> groupCreationValidationListeners;
private final AddMembers addMembers; private final AddMembers addMembers;
private final SystemGroupBackend systemGroupBackend; private final SystemGroupBackend systemGroupBackend;
private final boolean defaultVisibleToAll; private final boolean defaultVisibleToAll;
@@ -93,7 +93,7 @@ public class CreateGroup
GroupCache groupCache, GroupCache groupCache,
GroupsCollection groups, GroupsCollection groups,
GroupJson json, GroupJson json,
DynamicSet<GroupCreationValidationListener> groupCreationValidationListeners, PluginSetContext<GroupCreationValidationListener> groupCreationValidationListeners,
AddMembers addMembers, AddMembers addMembers,
SystemGroupBackend systemGroupBackend, SystemGroupBackend systemGroupBackend,
@GerritServerConfig Config cfg, @GerritServerConfig Config cfg,
@@ -158,12 +158,11 @@ public class CreateGroup
: Collections.<Account.Id>emptySet(); : Collections.<Account.Id>emptySet();
} }
for (GroupCreationValidationListener l : groupCreationValidationListeners) { try {
try { groupCreationValidationListeners.runEach(
l.validateNewGroup(args); l -> l.validateNewGroup(args), ValidationException.class);
} catch (ValidationException e) { } catch (ValidationException e) {
throw new ResourceConflictException(e.getMessage(), e); throw new ResourceConflictException(e.getMessage(), e);
}
} }
return json.format(new InternalGroupDescription(createGroup(args))); return json.format(new InternalGroupDescription(createGroup(args)));

View File

@@ -34,8 +34,6 @@ import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ProjectInfo; import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -60,6 +58,8 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.RepositoryCaseMismatchException; import com.google.gerrit.server.git.RepositoryCaseMismatchException;
import com.google.gerrit.server.git.meta.MetaDataUpdate; import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.CreateProjectArgs; import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.server.project.ProjectConfig;
@@ -98,10 +98,11 @@ public class CreateProject
private final Provider<ProjectsCollection> projectsCollection; private final Provider<ProjectsCollection> projectsCollection;
private final Provider<GroupsCollection> groupsCollection; private final Provider<GroupsCollection> groupsCollection;
private final DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners; private final PluginSetContext<ProjectCreationValidationListener>
projectCreationValidationListeners;
private final ProjectJson json; private final ProjectJson json;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final DynamicSet<NewProjectCreatedListener> createdListeners; private final PluginSetContext<NewProjectCreatedListener> createdListeners;
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final GroupBackend groupBackend; private final GroupBackend groupBackend;
private final ProjectOwnerGroupsProvider.Factory projectOwnerGroups; private final ProjectOwnerGroupsProvider.Factory projectOwnerGroups;
@@ -113,16 +114,16 @@ public class CreateProject
private final Provider<PutConfig> putConfig; private final Provider<PutConfig> putConfig;
private final AllProjectsName allProjects; private final AllProjectsName allProjects;
private final AllUsersName allUsers; private final AllUsersName allUsers;
private final DynamicItem<ProjectNameLockManager> lockManager; private final PluginItemContext<ProjectNameLockManager> lockManager;
@Inject @Inject
CreateProject( CreateProject(
Provider<ProjectsCollection> projectsCollection, Provider<ProjectsCollection> projectsCollection,
Provider<GroupsCollection> groupsCollection, Provider<GroupsCollection> groupsCollection,
ProjectJson json, ProjectJson json,
DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners, PluginSetContext<ProjectCreationValidationListener> projectCreationValidationListeners,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
DynamicSet<NewProjectCreatedListener> createdListeners, PluginSetContext<NewProjectCreatedListener> createdListeners,
ProjectCache projectCache, ProjectCache projectCache,
GroupBackend groupBackend, GroupBackend groupBackend,
ProjectOwnerGroupsProvider.Factory projectOwnerGroups, ProjectOwnerGroupsProvider.Factory projectOwnerGroups,
@@ -134,7 +135,7 @@ public class CreateProject
Provider<PutConfig> putConfig, Provider<PutConfig> putConfig,
AllProjectsName allProjects, AllProjectsName allProjects,
AllUsersName allUsers, AllUsersName allUsers,
DynamicItem<ProjectNameLockManager> lockManager) { PluginItemContext<ProjectNameLockManager> lockManager) {
this.projectsCollection = projectsCollection; this.projectsCollection = projectsCollection;
this.groupsCollection = groupsCollection; this.groupsCollection = groupsCollection;
this.projectCreationValidationListeners = projectCreationValidationListeners; this.projectCreationValidationListeners = projectCreationValidationListeners;
@@ -213,15 +214,14 @@ public class CreateProject
throw new BadRequestException(e.getMessage()); throw new BadRequestException(e.getMessage());
} }
Lock nameLock = lockManager.get().getLock(args.getProject()); Lock nameLock = lockManager.call(lockManager -> lockManager.getLock(args.getProject()));
nameLock.lock(); nameLock.lock();
try { try {
for (ProjectCreationValidationListener l : projectCreationValidationListeners) { try {
try { projectCreationValidationListeners.runEach(
l.validateNewProject(args); l -> l.validateNewProject(args), ValidationException.class);
} catch (ValidationException e) { } catch (ValidationException e) {
throw new ResourceConflictException(e.getMessage(), e); throw new ResourceConflictException(e.getMessage(), e);
}
} }
ProjectState projectState = createProject(args); ProjectState projectState = createProject(args);
@@ -392,18 +392,12 @@ public class CreateProject
} }
private void fire(Project.NameKey name, String head) { private void fire(Project.NameKey name, String head) {
if (!createdListeners.iterator().hasNext()) { if (createdListeners.isEmpty()) {
return; return;
} }
Event event = new Event(name, head); Event event = new Event(name, head);
for (NewProjectCreatedListener l : createdListeners) { createdListeners.runEach(l -> l.onNewProjectCreated(event));
try {
l.onNewProjectCreated(event);
} catch (RuntimeException e) {
logger.atWarning().withCause(e).log("Failure in NewProjectCreatedListener");
}
}
} }
static class Event extends AbstractNoNotifyEvent implements NewProjectCreatedListener.Event { static class Event extends AbstractNoNotifyEvent implements NewProjectCreatedListener.Event {

View File

@@ -15,10 +15,8 @@
package com.google.gerrit.server.restapi.project; package com.google.gerrit.server.restapi.project;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.projects.HeadInput; import com.google.gerrit.extensions.api.projects.HeadInput;
import com.google.gerrit.extensions.events.HeadUpdatedListener; import com.google.gerrit.extensions.events.HeadUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -32,6 +30,7 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
@@ -46,18 +45,16 @@ import org.eclipse.jgit.lib.Repository;
@Singleton @Singleton
public class SetHead implements RestModifyView<ProjectResource, HeadInput> { public class SetHead implements RestModifyView<ProjectResource, HeadInput> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final Provider<IdentifiedUser> identifiedUser; private final Provider<IdentifiedUser> identifiedUser;
private final DynamicSet<HeadUpdatedListener> headUpdatedListeners; private final PluginSetContext<HeadUpdatedListener> headUpdatedListeners;
private final PermissionBackend permissionBackend; private final PermissionBackend permissionBackend;
@Inject @Inject
SetHead( SetHead(
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
Provider<IdentifiedUser> identifiedUser, Provider<IdentifiedUser> identifiedUser,
DynamicSet<HeadUpdatedListener> headUpdatedListeners, PluginSetContext<HeadUpdatedListener> headUpdatedListeners,
PermissionBackend permissionBackend) { PermissionBackend permissionBackend) {
this.repoManager = repoManager; this.repoManager = repoManager;
this.identifiedUser = identifiedUser; this.identifiedUser = identifiedUser;
@@ -119,17 +116,12 @@ public class SetHead implements RestModifyView<ProjectResource, HeadInput> {
} }
private void fire(Project.NameKey nameKey, String oldHead, String newHead) { private void fire(Project.NameKey nameKey, String oldHead, String newHead) {
if (!headUpdatedListeners.iterator().hasNext()) { if (headUpdatedListeners.isEmpty()) {
return; return;
} }
Event event = new Event(nameKey, oldHead, newHead); Event event = new Event(nameKey, oldHead, newHead);
for (HeadUpdatedListener l : headUpdatedListeners) { headUpdatedListeners.runEach(l -> l.onHeadUpdated(event));
try {
l.onHeadUpdated(event);
} catch (RuntimeException e) {
logger.atWarning().withCause(e).log("Failure in HeadUpdatedListener");
}
}
} }
static class Event extends AbstractNoNotifyEvent implements HeadUpdatedListener.Event { static class Event extends AbstractNoNotifyEvent implements HeadUpdatedListener.Event {

View File

@@ -25,9 +25,11 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.permissions.ChangePermission; import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginContext;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
@@ -120,7 +122,9 @@ public class MergeSuperSet {
if (wholeTopicEnabled(cfg)) { if (wholeTopicEnabled(cfg)) {
return completeChangeSetIncludingTopics(db, changeSet, user); return completeChangeSetIncludingTopics(db, changeSet, user);
} }
return mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user); try (TraceContext traceContext = PluginContext.newTrace(mergeSuperSetComputation)) {
return mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
}
} finally { } finally {
if (closeOrm && orm != null) { if (closeOrm && orm != null) {
orm.close(); orm.close();
@@ -195,7 +199,9 @@ public class MergeSuperSet {
do { do {
oldSeen = seen; oldSeen = seen;
changeSet = mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user); try (TraceContext traceContext = PluginContext.newTrace(mergeSuperSetComputation)) {
changeSet = mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
}
changeSet = topicClosure(db, changeSet, user, topicsSeen, visibleTopicsSeen); changeSet = topicClosure(db, changeSet, user, topicsSeen, visibleTopicsSeen);
seen = topicsSeen.size() + visibleTopicsSeen.size(); seen = topicsSeen.size() + visibleTopicsSeen.size();
} while (seen != oldSeen); } while (seen != oldSeen);

View File

@@ -35,6 +35,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroup.UUID; import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.testing.GerritBaseTests; import com.google.gerrit.testing.GerritBaseTests;
import java.util.Set; import java.util.Set;
import org.easymock.IAnswer; import org.easymock.IAnswer;
@@ -56,7 +57,7 @@ public class UniversalGroupBackendTest extends GerritBaseTests {
replay(user); replay(user);
backends = new DynamicSet<>(); backends = new DynamicSet<>();
backends.add("gerrit", new SystemGroupBackend(new Config())); backends.add("gerrit", new SystemGroupBackend(new Config()));
backend = new UniversalGroupBackend(backends); backend = new UniversalGroupBackend(new PluginSetContext<>(backends));
} }
@Test @Test
@@ -124,7 +125,7 @@ public class UniversalGroupBackendTest extends GerritBaseTests {
backends = new DynamicSet<>(); backends = new DynamicSet<>();
backends.add("gerrit", backend); backends.add("gerrit", backend);
backend = new UniversalGroupBackend(backends); backend = new UniversalGroupBackend(new PluginSetContext<>(backends));
GroupMembership checker = backend.membershipsOf(member); GroupMembership checker = backend.membershipsOf(member);
assertFalse(checker.contains(REGISTERED_USERS)); assertFalse(checker.contains(REGISTERED_USERS));