User memoized supplier pattern to reduce resource consumption

It's pointless to retrieve the whole data from the database and git
backends when it's thrown away anyway. Use memoized supplier pattern
to only retrieve the data if really needed. We need memoization in
this context to ensure, that when the data is retrieved, it is done
only once and not multiple times in different plugins and in hooks
firing tool chain.

This change breaks plugins that consume change events. However, given
that only few plugins, e.g. reviewers, are affected by this change,
this API change is justfied, as it's substantially decreasing the
load on the gerrit site.

One side effect of this change is stream event JSON serialization. Use
custom JSON serializer to resolve supplier with their content.

Test Plan:

Non retrieval of data wth default gerrit install + reviewers plugin.

1. install gerrit with all core plugins
2. install reviewers plugin
3. create a group_100 that contains 100 users
4. add a reviewer to a change and select the group_100

Without this change hundreds of SQL select statements are executed in
vain. That's because 100 ChangeEvents instances are created and multiple
SQL select statements are executed per instance. With this change exact
0 select SQL statements are executed. Memoized suppliers are created
but not used.

Change-Id: I10e257be37ff60276e4187659926fe32e5be136b
Reported-By: Luca Milanesio <luca.milanesio@gmail.com>
This commit is contained in:
David Ostrovsky 2015-12-22 19:08:40 +01:00 committed by Shawn Pearce
parent 12380e0a0d
commit 2605168713
17 changed files with 418 additions and 181 deletions

View File

@ -106,7 +106,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
public void onEvent(Event event) { public void onEvent(Event event) {
if (event instanceof ChangeMergedEvent) { if (event instanceof ChangeMergedEvent) {
ChangeMergedEvent changeMergedEvent = (ChangeMergedEvent) event; ChangeMergedEvent changeMergedEvent = (ChangeMergedEvent) event;
mergeResults.put(changeMergedEvent.change.number, mergeResults.put(changeMergedEvent.change.get().number,
changeMergedEvent.newRev); changeMergedEvent.newRev);
} }
} }

View File

@ -14,6 +14,10 @@
package com.google.gerrit.common; package com.google.gerrit.common;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Throwables;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelType;
@ -34,8 +38,11 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.AnonymousCowardName; import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute; import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute; import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.events.ChangeAbandonedEvent; import com.google.gerrit.server.events.ChangeAbandonedEvent;
import com.google.gerrit.server.events.ChangeMergedEvent; import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent; import com.google.gerrit.server.events.ChangeRestoredEvent;
@ -174,46 +181,46 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
private final DynamicSet<EventListener> unrestrictedListeners; private final DynamicSet<EventListener> unrestrictedListeners;
/** Path of the new patchset hook. */ /** Path of the new patchset hook. */
private final Path patchsetCreatedHook; private final Optional<Path> patchsetCreatedHook;
/** Path of the draft published hook. */ /** Path of the draft published hook. */
private final Path draftPublishedHook; private final Optional<Path> draftPublishedHook;
/** Path of the new comments hook. */ /** Path of the new comments hook. */
private final Path commentAddedHook; private final Optional<Path> commentAddedHook;
/** Path of the change merged hook. */ /** Path of the change merged hook. */
private final Path changeMergedHook; private final Optional<Path> changeMergedHook;
/** Path of the merge failed hook. */ /** Path of the merge failed hook. */
private final Path mergeFailedHook; private final Optional<Path> mergeFailedHook;
/** Path of the change abandoned hook. */ /** Path of the change abandoned hook. */
private final Path changeAbandonedHook; private final Optional<Path> changeAbandonedHook;
/** Path of the change restored hook. */ /** Path of the change restored hook. */
private final Path changeRestoredHook; private final Optional<Path> changeRestoredHook;
/** Path of the ref updated hook. */ /** Path of the ref updated hook. */
private final Path refUpdatedHook; private final Optional<Path> refUpdatedHook;
/** Path of the reviewer added hook. */ /** Path of the reviewer added hook. */
private final Path reviewerAddedHook; private final Optional<Path> reviewerAddedHook;
/** Path of the topic changed hook. */ /** Path of the topic changed hook. */
private final Path topicChangedHook; private final Optional<Path> topicChangedHook;
/** Path of the cla signed hook. */ /** Path of the cla signed hook. */
private final Path claSignedHook; private final Optional<Path> claSignedHook;
/** Path of the update hook. */ /** Path of the update hook. */
private final Path refUpdateHook; private final Optional<Path> refUpdateHook;
/** Path of the hashtags changed hook */ /** Path of the hashtags changed hook */
private final Path hashtagsChangedHook; private final Optional<Path> hashtagsChangedHook;
/** Path of the project created hook. */ /** Path of the project created hook. */
private final Path projectCreatedHook; private final Optional<Path> projectCreatedHook;
private final String anonymousCowardName; private final String anonymousCowardName;
@ -297,10 +304,11 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
.build()); .build());
} }
private static Path hook(Config config, Path path, String name) { private static Optional<Path> hook(Config config, Path path, String name) {
String setting = name.replace("-", "") + "hook"; String setting = name.replace("-", "") + "hook";
String value = config.getString("hooks", null, setting); String value = config.getString("hooks", null, setting);
return path.resolve(value != null ? value : name); Path p = path.resolve(value != null ? value : name);
return Files.exists(p) ? Optional.of(p) : Optional.<Path>absent();
} }
@Override @Override
@ -335,16 +343,6 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
} }
} }
private PatchSetAttribute asPatchSetAttribute(Change change,
PatchSet patchSet, ReviewDb db) throws OrmException {
try (Repository repo = repoManager.openRepository(change.getProject());
RevWalk revWalk = new RevWalk(repo)) {
return eventFactory.asPatchSetAttribute(db, revWalk, patchSet);
} catch (IOException e) {
throw new OrmException(e);
}
}
/** /**
* Fire the update hook * Fire the update hook
* *
@ -352,6 +350,9 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
@Override @Override
public HookResult doRefUpdateHook(Project project, String refname, public HookResult doRefUpdateHook(Project project, String refname,
Account uploader, ObjectId oldId, ObjectId newId) { Account uploader, ObjectId oldId, ObjectId newId) {
if (!refUpdateHook.isPresent()) {
return null;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--project", project.getName()); addArg(args, "--project", project.getName());
@ -368,8 +369,13 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
ProjectCreatedEvent event = new ProjectCreatedEvent(); ProjectCreatedEvent event = new ProjectCreatedEvent();
event.projectName = project.get(); event.projectName = project.get();
event.headName = headName; event.headName = headName;
fireEvent(project, event); fireEvent(project, event);
if (!projectCreatedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--project", project.get()); addArg(args, "--project", project.get());
addArg(args, "--head", headName); addArg(args, "--head", headName);
@ -382,32 +388,42 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
* *
* @param change The change itself. * @param change The change itself.
* @param patchSet The Patchset that was created. * @param patchSet The Patchset that was created.
* @param db Review database.
* @throws OrmException * @throws OrmException
*/ */
@Override @Override
public void doPatchsetCreatedHook(Change change, PatchSet patchSet, public void doPatchsetCreatedHook(Change change,
ReviewDb db) throws OrmException { PatchSet patchSet, ReviewDb db) throws OrmException {
PatchSetCreatedEvent event = new PatchSetCreatedEvent(); PatchSetCreatedEvent event = new PatchSetCreatedEvent();
AccountState uploader = accountCache.get(patchSet.getUploader()); Supplier<AccountState> uploader =
AccountState owner = accountCache.get(change.getOwner()); getAccountSupplier(patchSet.getUploader());
Supplier<AccountState> owner = getAccountSupplier(change.getOwner());
event.change = changeAttributeSupplier(change);
event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.uploader = accountAttributeSupplier(uploader);
event.change = eventFactory.asChangeAttribute(db, change);
event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.uploader = eventFactory.asAccountAttribute(uploader.getAccount());
fireEvent(change, event, db); fireEvent(change, event, db);
if (!patchsetCreatedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id);
ChangeAttribute c = event.change.get();
PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change", c.id);
addArg(args, "--is-draft", String.valueOf(patchSet.isDraft())); addArg(args, "--is-draft", String.valueOf(patchSet.isDraft()));
addArg(args, "--kind", String.valueOf(event.patchSet.kind)); addArg(args, "--kind", String.valueOf(ps.kind));
addArg(args, "--change-url", event.change.url); addArg(args, "--change-url", c.url);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change-owner", getDisplayName(owner.get().getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--project", c.project);
addArg(args, "--branch", event.change.branch); addArg(args, "--branch", c.branch);
addArg(args, "--topic", event.change.topic); addArg(args, "--topic", c.topic);
addArg(args, "--uploader", getDisplayName(uploader.getAccount())); addArg(args, "--uploader", getDisplayName(uploader.get().getAccount()));
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--commit", ps.revision);
addArg(args, "--patchset", event.patchSet.number); addArg(args, "--patchset", ps.number);
runHook(change.getProject(), patchsetCreatedHook, args); runHook(change.getProject(), patchsetCreatedHook, args);
} }
@ -416,62 +432,88 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
public void doDraftPublishedHook(Change change, PatchSet patchSet, public void doDraftPublishedHook(Change change, PatchSet patchSet,
ReviewDb db) throws OrmException { ReviewDb db) throws OrmException {
DraftPublishedEvent event = new DraftPublishedEvent(); DraftPublishedEvent event = new DraftPublishedEvent();
AccountState uploader = accountCache.get(patchSet.getUploader()); Supplier<AccountState> uploader =
AccountState owner = accountCache.get(change.getOwner()); getAccountSupplier(patchSet.getUploader());
Supplier<AccountState> owner = getAccountSupplier(change.getOwner());
event.change = changeAttributeSupplier(change);
event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.uploader = accountAttributeSupplier(uploader);
event.change = eventFactory.asChangeAttribute(db, change);
event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.uploader = eventFactory.asAccountAttribute(uploader.getAccount());
fireEvent(change, event, db); fireEvent(change, event, db);
if (!draftPublishedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change-url", event.change.url); PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--change", c.id);
addArg(args, "--branch", event.change.branch); addArg(args, "--change-url", c.url);
addArg(args, "--topic", event.change.topic); addArg(args, "--change-owner", getDisplayName(owner.get().getAccount()));
addArg(args, "--uploader", getDisplayName(uploader.getAccount())); addArg(args, "--project", c.project);
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--branch", c.branch);
addArg(args, "--patchset", event.patchSet.number); addArg(args, "--topic", c.topic);
addArg(args, "--uploader", getDisplayName(uploader.get().getAccount()));
addArg(args, "--commit", ps.revision);
addArg(args, "--patchset", ps.number);
runHook(change.getProject(), draftPublishedHook, args); runHook(change.getProject(), draftPublishedHook, args);
} }
@Override @Override
public void doCommentAddedHook(Change change, Account account, public void doCommentAddedHook(final Change change, Account account,
PatchSet patchSet, String comment, Map<String, Short> approvals, PatchSet patchSet, String comment, final Map<String, Short> approvals,
ReviewDb db) throws OrmException { ReviewDb db) throws OrmException {
CommentAddedEvent event = new CommentAddedEvent(); CommentAddedEvent event = new CommentAddedEvent();
AccountState owner = accountCache.get(change.getOwner()); Supplier<AccountState> owner = getAccountSupplier(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.author = eventFactory.asAccountAttribute(account); event.author = accountAttributeSupplier(account);
event.patchSet = asPatchSetAttribute(change, patchSet, db); event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.comment = comment; event.comment = comment;
event.approvals = Suppliers.memoize(
LabelTypes labelTypes = projectCache.get(change.getProject()).getLabelTypes(); new Supplier<ApprovalAttribute[]>() {
if (approvals.size() > 0) { @Override
event.approvals = new ApprovalAttribute[approvals.size()]; public ApprovalAttribute[] get() {
int i = 0; LabelTypes labelTypes = projectCache.get(
for (Map.Entry<String, Short> approval : approvals.entrySet()) { change.getProject()).getLabelTypes();
event.approvals[i++] = getApprovalAttribute(labelTypes, approval); if (approvals.size() > 0) {
} ApprovalAttribute[] r = new ApprovalAttribute[approvals.size()];
} int i = 0;
for (Map.Entry<String, Short> approval : approvals.entrySet()) {
r[i++] = getApprovalAttribute(labelTypes, approval);
}
return r;
}
return null;
}
});
fireEvent(change, event, db); fireEvent(change, event, db);
if (!commentAddedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change", c.id);
addArg(args, "--is-draft", patchSet.isDraft() ? "true" : "false"); addArg(args, "--is-draft", patchSet.isDraft() ? "true" : "false");
addArg(args, "--change-url", event.change.url); addArg(args, "--change-url", c.url);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change-owner", getDisplayName(owner.get().getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--project", c.project);
addArg(args, "--branch", event.change.branch); addArg(args, "--branch", c.branch);
addArg(args, "--topic", event.change.topic); addArg(args, "--topic", c.topic);
addArg(args, "--author", getDisplayName(account)); addArg(args, "--author", getDisplayName(account));
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--commit", ps.revision);
addArg(args, "--comment", comment == null ? "" : comment); addArg(args, "--comment", comment == null ? "" : comment);
LabelTypes labelTypes = projectCache.get(
change.getProject()).getLabelTypes();
for (Map.Entry<String, Short> approval : approvals.entrySet()) { for (Map.Entry<String, Short> approval : approvals.entrySet()) {
LabelType lt = labelTypes.byLabel(approval.getKey()); LabelType lt = labelTypes.byLabel(approval.getKey());
if (lt != null) { if (lt != null) {
@ -487,23 +529,31 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
PatchSet patchSet, ReviewDb db, String mergeResultRev) PatchSet patchSet, ReviewDb db, String mergeResultRev)
throws OrmException { throws OrmException {
ChangeMergedEvent event = new ChangeMergedEvent(); ChangeMergedEvent event = new ChangeMergedEvent();
AccountState owner = accountCache.get(change.getOwner()); Supplier<AccountState> owner = getAccountSupplier(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.submitter = eventFactory.asAccountAttribute(account); event.submitter = accountAttributeSupplier(account);
event.patchSet = asPatchSetAttribute(change, patchSet, db); event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.newRev = mergeResultRev; event.newRev = mergeResultRev;
fireEvent(change, event, db); fireEvent(change, event, db);
if (!changeMergedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change-url", event.change.url); PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--change", c.id);
addArg(args, "--branch", event.change.branch); addArg(args, "--change-url", c.url);
addArg(args, "--topic", event.change.topic); addArg(args, "--change-owner", getDisplayName(owner.get().getAccount()));
addArg(args, "--project", c.project);
addArg(args, "--branch", c.branch);
addArg(args, "--topic", c.topic);
addArg(args, "--submitter", getDisplayName(account)); addArg(args, "--submitter", getDisplayName(account));
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--commit", ps.revision);
addArg(args, "--newrev", mergeResultRev); addArg(args, "--newrev", mergeResultRev);
runHook(change.getProject(), changeMergedHook, args); runHook(change.getProject(), changeMergedHook, args);
@ -514,23 +564,31 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
PatchSet patchSet, String reason, PatchSet patchSet, String reason,
ReviewDb db) throws OrmException { ReviewDb db) throws OrmException {
MergeFailedEvent event = new MergeFailedEvent(); MergeFailedEvent event = new MergeFailedEvent();
AccountState owner = accountCache.get(change.getOwner()); Supplier<AccountState> owner = getAccountSupplier(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.submitter = eventFactory.asAccountAttribute(account); event.submitter = accountAttributeSupplier(account);
event.patchSet = asPatchSetAttribute(change, patchSet, db); event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.reason = reason; event.reason = reason;
fireEvent(change, event, db); fireEvent(change, event, db);
if (!mergeFailedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change-url", event.change.url); PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--change", c.id);
addArg(args, "--branch", event.change.branch); addArg(args, "--change-url", c.url);
addArg(args, "--topic", event.change.topic); addArg(args, "--change-owner", getDisplayName(owner.get().getAccount()));
addArg(args, "--project", c.project);
addArg(args, "--branch", c.branch);
addArg(args, "--topic", c.topic);
addArg(args, "--submitter", getDisplayName(account)); addArg(args, "--submitter", getDisplayName(account));
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--commit", ps.revision);
addArg(args, "--reason", reason == null ? "" : reason); addArg(args, "--reason", reason == null ? "" : reason);
runHook(change.getProject(), mergeFailedHook, args); runHook(change.getProject(), mergeFailedHook, args);
@ -543,21 +601,29 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
ChangeAbandonedEvent event = new ChangeAbandonedEvent(); ChangeAbandonedEvent event = new ChangeAbandonedEvent();
AccountState owner = accountCache.get(change.getOwner()); AccountState owner = accountCache.get(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.abandoner = eventFactory.asAccountAttribute(account); event.abandoner = accountAttributeSupplier(account);
event.patchSet = asPatchSetAttribute(change, patchSet, db); event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.reason = reason; event.reason = reason;
fireEvent(change, event, db); fireEvent(change, event, db);
if (!changeAbandonedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change-url", event.change.url); PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change", c.id);
addArg(args, "--change-url", c.url);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--project", c.project);
addArg(args, "--branch", event.change.branch); addArg(args, "--branch", c.branch);
addArg(args, "--topic", event.change.topic); addArg(args, "--topic", c.topic);
addArg(args, "--abandoner", getDisplayName(account)); addArg(args, "--abandoner", getDisplayName(account));
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--commit", ps.revision);
addArg(args, "--reason", reason == null ? "" : reason); addArg(args, "--reason", reason == null ? "" : reason);
runHook(change.getProject(), changeAbandonedHook, args); runHook(change.getProject(), changeAbandonedHook, args);
@ -570,21 +636,29 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
ChangeRestoredEvent event = new ChangeRestoredEvent(); ChangeRestoredEvent event = new ChangeRestoredEvent();
AccountState owner = accountCache.get(change.getOwner()); AccountState owner = accountCache.get(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.restorer = eventFactory.asAccountAttribute(account); event.restorer = accountAttributeSupplier(account);
event.patchSet = asPatchSetAttribute(change, patchSet, db); event.patchSet = patchSetAttributeSupplier(change, patchSet);
event.reason = reason; event.reason = reason;
fireEvent(change, event, db); fireEvent(change, event, db);
if (!changeRestoredHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change-url", event.change.url); PatchSetAttribute ps = event.patchSet.get();
addArg(args, "--change", c.id);
addArg(args, "--change-url", c.url);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--project", c.project);
addArg(args, "--branch", event.change.branch); addArg(args, "--branch", c.branch);
addArg(args, "--topic", event.change.topic); addArg(args, "--topic", c.topic);
addArg(args, "--restorer", getDisplayName(account)); addArg(args, "--restorer", getDisplayName(account));
addArg(args, "--commit", event.patchSet.revision); addArg(args, "--commit", ps.revision);
addArg(args, "--reason", reason == null ? "" : reason); addArg(args, "--reason", reason == null ? "" : reason);
runHook(change.getProject(), changeRestoredHook, args); runHook(change.getProject(), changeRestoredHook, args);
@ -598,21 +672,33 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
} }
@Override @Override
public void doRefUpdatedHook(Branch.NameKey refName, ObjectId oldId, public void doRefUpdatedHook(final Branch.NameKey refName,
ObjectId newId, Account account) { final ObjectId oldId, final ObjectId newId, Account account) {
RefUpdatedEvent event = new RefUpdatedEvent(); RefUpdatedEvent event = new RefUpdatedEvent();
if (account != null) { if (account != null) {
event.submitter = eventFactory.asAccountAttribute(account); event.submitter = accountAttributeSupplier(account);
} }
event.refUpdate = eventFactory.asRefUpdateAttribute(oldId, newId, refName); event.refUpdate = Suppliers.memoize(
new Supplier<RefUpdateAttribute>() {
@Override
public RefUpdateAttribute get() {
return eventFactory.asRefUpdateAttribute(oldId, newId, refName);
}
});
fireEvent(refName, event); fireEvent(refName, event);
if (!refUpdatedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--oldrev", event.refUpdate.oldRev); RefUpdateAttribute r = event.refUpdate.get();
addArg(args, "--newrev", event.refUpdate.newRev); addArg(args, "--oldrev", r.oldRev);
addArg(args, "--refname", event.refUpdate.refName); addArg(args, "--newrev", r.newRev);
addArg(args, "--project", event.refUpdate.project); addArg(args, "--refname", r.refName);
addArg(args, "--project", r.project);
if (account != null) { if (account != null) {
addArg(args, "--submitter", getDisplayName(account)); addArg(args, "--submitter", getDisplayName(account));
} }
@ -624,19 +710,26 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
public void doReviewerAddedHook(Change change, Account account, public void doReviewerAddedHook(Change change, Account account,
PatchSet patchSet, ReviewDb db) throws OrmException { PatchSet patchSet, ReviewDb db) throws OrmException {
ReviewerAddedEvent event = new ReviewerAddedEvent(); ReviewerAddedEvent event = new ReviewerAddedEvent();
AccountState owner = accountCache.get(change.getOwner()); Supplier<AccountState> owner = getAccountSupplier(change.getOwner());
event.change = changeAttributeSupplier(change);
event.patchSet = null;//patchSetAttributeSupplier(change, patchSet, db);
event.reviewer = null;//accountAttributeSupplier(account);
event.change = eventFactory.asChangeAttribute(db, change);
event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.reviewer = eventFactory.asAccountAttribute(account);
fireEvent(change, event, db); fireEvent(change, event, db);
if (!reviewerAddedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change-url", event.change.url);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change", c.id);
addArg(args, "--project", event.change.project); addArg(args, "--change-url", c.url);
addArg(args, "--branch", event.change.branch); addArg(args, "--change-owner", getDisplayName(owner.get().getAccount()));
addArg(args, "--project", c.project);
addArg(args, "--branch", c.branch);
addArg(args, "--reviewer", getDisplayName(account)); addArg(args, "--reviewer", getDisplayName(account));
runHook(change.getProject(), reviewerAddedHook, args); runHook(change.getProject(), reviewerAddedHook, args);
@ -649,19 +742,26 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
TopicChangedEvent event = new TopicChangedEvent(); TopicChangedEvent event = new TopicChangedEvent();
AccountState owner = accountCache.get(change.getOwner()); AccountState owner = accountCache.get(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.changer = eventFactory.asAccountAttribute(account); event.changer = accountAttributeSupplier(account);
event.oldTopic = oldTopic; event.oldTopic = oldTopic;
fireEvent(change, event, db); fireEvent(change, event, db);
if (!topicChangedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change", c.id);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--project", c.project);
addArg(args, "--branch", event.change.branch); addArg(args, "--branch", c.branch);
addArg(args, "--changer", getDisplayName(account)); addArg(args, "--changer", getDisplayName(account));
addArg(args, "--old-topic", oldTopic); addArg(args, "--old-topic", oldTopic);
addArg(args, "--new-topic", event.change.topic); addArg(args, "--new-topic", c.topic);
runHook(change.getProject(), topicChangedHook, args); runHook(change.getProject(), topicChangedHook, args);
} }
@ -681,19 +781,25 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
HashtagsChangedEvent event = new HashtagsChangedEvent(); HashtagsChangedEvent event = new HashtagsChangedEvent();
AccountState owner = accountCache.get(change.getOwner()); AccountState owner = accountCache.get(change.getOwner());
event.change = eventFactory.asChangeAttribute(db, change); event.change = changeAttributeSupplier(change);
event.editor = eventFactory.asAccountAttribute(account); event.editor = accountAttributeSupplier(account);
event.hashtags = hashtagArray(hashtags); event.hashtags = hashtagArray(hashtags);
event.added = hashtagArray(added); event.added = hashtagArray(added);
event.removed = hashtagArray(removed); event.removed = hashtagArray(removed);
fireEvent(change, event, db); fireEvent(change, event, db);
if (!hashtagsChangedHook.isPresent()) {
return;
}
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--change", event.change.id); ChangeAttribute c = event.change.get();
addArg(args, "--change", c.id);
addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--change-owner", getDisplayName(owner.getAccount()));
addArg(args, "--project", event.change.project); addArg(args, "--project", c.project);
addArg(args, "--branch", event.change.branch); addArg(args, "--branch", c.branch);
addArg(args, "--editor", getDisplayName(account)); addArg(args, "--editor", getDisplayName(account));
if (hashtags != null) { if (hashtags != null) {
for (String hashtag : hashtags) { for (String hashtag : hashtags) {
@ -715,6 +821,10 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
@Override @Override
public void doClaSignupHook(Account account, String claName) { public void doClaSignupHook(Account account, String claName) {
if (!claSignedHook.isPresent()) {
return;
}
if (account != null) { if (account != null) {
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
addArg(args, "--submitter", getDisplayName(account)); addArg(args, "--submitter", getDisplayName(account));
@ -736,6 +846,67 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
fireEvent(branchName, event); fireEvent(branchName, event);
} }
private Supplier<AccountState> getAccountSupplier(
final Account.Id account) {
return Suppliers.memoize(
new Supplier<AccountState>() {
@Override
public AccountState get() {
return accountCache.get(account);
}
});
}
private Supplier<AccountAttribute> accountAttributeSupplier(
final Supplier<AccountState> s) {
return Suppliers.memoize(
new Supplier<AccountAttribute>() {
@Override
public AccountAttribute get() {
return eventFactory.asAccountAttribute(s.get().getAccount());
}
});
}
private Supplier<AccountAttribute> accountAttributeSupplier(
final Account account) {
return Suppliers.memoize(
new Supplier<AccountAttribute>() {
@Override
public AccountAttribute get() {
return eventFactory.asAccountAttribute(account);
}
});
}
private Supplier<PatchSetAttribute> patchSetAttributeSupplier(
final Change change, final PatchSet patchSet) {
return Suppliers.memoize(
new Supplier<PatchSetAttribute>() {
@Override
public PatchSetAttribute get() {
try (Repository repo
= repoManager.openRepository(change.getProject());
RevWalk revWalk = new RevWalk(repo)) {
return eventFactory.asPatchSetAttribute(revWalk, patchSet);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
});
}
private Supplier<ChangeAttribute> changeAttributeSupplier(
final Change change) {
return Suppliers.memoize(
new Supplier<ChangeAttribute>() {
@Override
public ChangeAttribute get() {
return eventFactory.asChangeAttribute(change);
}
});
}
private void fireEventForUnrestrictedListeners(com.google.gerrit.server.events.Event event) { private void fireEventForUnrestrictedListeners(com.google.gerrit.server.events.Event event) {
for (EventListener listener : unrestrictedListeners) { for (EventListener listener : unrestrictedListeners) {
listener.onEvent(event); listener.onEvent(event);
@ -851,27 +1022,27 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
* @param hook the hook to execute. * @param hook the hook to execute.
* @param args Arguments to use to run the hook. * @param args Arguments to use to run the hook.
*/ */
private synchronized void runHook(Project.NameKey project, Path hook, private synchronized void runHook(Project.NameKey project, Optional<Path> hook,
List<String> args) { List<String> args) {
if (project != null && Files.exists(hook)) { if (project != null && hook.isPresent()) {
hookQueue.execute(new AsyncHookTask(project, hook, args)); hookQueue.execute(new AsyncHookTask(project, hook.get(), args));
} }
} }
private synchronized void runHook(Path hook, List<String> args) { private synchronized void runHook(Optional<Path> hook, List<String> args) {
if (Files.exists(hook)) { if (hook.isPresent()) {
hookQueue.execute(new AsyncHookTask(null, hook, args)); hookQueue.execute(new AsyncHookTask(null, hook.get(), args));
} }
} }
private HookResult runSyncHook(Project.NameKey project, private HookResult runSyncHook(Project.NameKey project,
Path hook, List<String> args) { Optional<Path> hook, List<String> args) {
if (!Files.exists(hook)) { if (!hook.isPresent()) {
return null; return null;
} }
SyncHookTask syncHook = new SyncHookTask(project, hook, args); SyncHookTask syncHook = new SyncHookTask(project, hook.get(), args);
FutureTask<HookResult> task = new FutureTask<>(syncHook); FutureTask<HookResult> task = new FutureTask<>(syncHook);
syncHookThreadPool.execute(task); syncHookThreadPool.execute(task);
@ -881,10 +1052,10 @@ public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
try { try {
return task.get(syncHookTimeout, TimeUnit.SECONDS); return task.get(syncHookTimeout, TimeUnit.SECONDS);
} catch (TimeoutException e) { } catch (TimeoutException e) {
message = "Synchronous hook timed out " + hook.toAbsolutePath(); message = "Synchronous hook timed out " + hook.get().toAbsolutePath();
log.error(message); log.error(message);
} catch (Exception e) { } catch (Exception e) {
message = "Error running hook " + hook.toAbsolutePath(); message = "Error running hook " + hook.get().toAbsolutePath();
log.error(message, e); log.error(message, e);
} }

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class ChangeAbandonedEvent extends PatchSetEvent { public class ChangeAbandonedEvent extends PatchSetEvent {
public AccountAttribute abandoner; public Supplier<AccountAttribute> abandoner;
public String reason; public String reason;
public ChangeAbandonedEvent() { public ChangeAbandonedEvent() {

View File

@ -14,13 +14,14 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
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;
import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.data.ChangeAttribute; import com.google.gerrit.server.data.ChangeAttribute;
public abstract class ChangeEvent extends RefEvent { public abstract class ChangeEvent extends RefEvent {
public ChangeAttribute change; public Supplier<ChangeAttribute> change;
protected ChangeEvent(String type) { protected ChangeEvent(String type) {
super(type); super(type);
@ -28,15 +29,15 @@ public abstract class ChangeEvent extends RefEvent {
@Override @Override
public Project.NameKey getProjectNameKey() { public Project.NameKey getProjectNameKey() {
return new Project.NameKey(change.project); return new Project.NameKey(change.get().project);
} }
@Override @Override
public String getRefName() { public String getRefName() {
return RefNames.fullName(change.branch); return RefNames.fullName(change.get().branch);
} }
public Change.Key getChangeKey() { public Change.Key getChangeKey() {
return new Change.Key(change.id); return new Change.Key(change.get().id);
} }
} }

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class ChangeMergedEvent extends PatchSetEvent { public class ChangeMergedEvent extends PatchSetEvent {
public AccountAttribute submitter; public Supplier<AccountAttribute> submitter;
public String newRev; public String newRev;
public ChangeMergedEvent() { public ChangeMergedEvent() {

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class ChangeRestoredEvent extends PatchSetEvent { public class ChangeRestoredEvent extends PatchSetEvent {
public AccountAttribute restorer; public Supplier<AccountAttribute> restorer;
public String reason; public String reason;
public ChangeRestoredEvent () { public ChangeRestoredEvent () {

View File

@ -14,12 +14,13 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute; import com.google.gerrit.server.data.ApprovalAttribute;
public class CommentAddedEvent extends PatchSetEvent { public class CommentAddedEvent extends PatchSetEvent {
public AccountAttribute author; public Supplier<AccountAttribute> author;
public ApprovalAttribute[] approvals; public Supplier<ApprovalAttribute[]> approvals;
public String comment; public String comment;
public CommentAddedEvent() { public CommentAddedEvent() {

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class DraftPublishedEvent extends PatchSetEvent { public class DraftPublishedEvent extends PatchSetEvent {
public AccountAttribute uploader; public Supplier<AccountAttribute> uploader;
public DraftPublishedEvent() { public DraftPublishedEvent() {
super("draft-published"); super("draft-published");

View File

@ -61,6 +61,7 @@ import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery; import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@ -92,6 +93,7 @@ public class EventFactory {
private final ApprovalsUtil approvalsUtil; private final ApprovalsUtil approvalsUtil;
private final ChangeKindCache changeKindCache; private final ChangeKindCache changeKindCache;
private final Provider<InternalChangeQuery> queryProvider; private final Provider<InternalChangeQuery> queryProvider;
private final SchemaFactory<ReviewDb> schema;
@Inject @Inject
EventFactory(AccountCache accountCache, EventFactory(AccountCache accountCache,
@ -102,7 +104,8 @@ public class EventFactory {
ChangeData.Factory changeDataFactory, ChangeData.Factory changeDataFactory,
ApprovalsUtil approvalsUtil, ApprovalsUtil approvalsUtil,
ChangeKindCache changeKindCache, ChangeKindCache changeKindCache,
Provider<InternalChangeQuery> queryProvider) { Provider<InternalChangeQuery> queryProvider,
SchemaFactory<ReviewDb> schema) {
this.accountCache = accountCache; this.accountCache = accountCache;
this.urlProvider = urlProvider; this.urlProvider = urlProvider;
this.patchListCache = patchListCache; this.patchListCache = patchListCache;
@ -112,6 +115,7 @@ public class EventFactory {
this.approvalsUtil = approvalsUtil; this.approvalsUtil = approvalsUtil;
this.changeKindCache = changeKindCache; this.changeKindCache = changeKindCache;
this.queryProvider = queryProvider; this.queryProvider = queryProvider;
this.schema = schema;
} }
/** /**
@ -121,6 +125,23 @@ public class EventFactory {
* @param change * @param change
* @return object suitable for serialization to JSON * @return object suitable for serialization to JSON
*/ */
public ChangeAttribute asChangeAttribute(Change change) {
try (ReviewDb db = schema.open()) {
return asChangeAttribute(db, change);
} catch (OrmException e) {
log.error("Cannot open database connection", e);
return new ChangeAttribute();
}
}
/**
* Create a ChangeAttribute for the given change suitable for serialization to
* JSON.
*
* @param db Review database
* @param change
* @return object suitable for serialization to JSON
*/
public ChangeAttribute asChangeAttribute(ReviewDb db, Change change) { public ChangeAttribute asChangeAttribute(ReviewDb db, Change change) {
ChangeAttribute a = new ChangeAttribute(); ChangeAttribute a = new ChangeAttribute();
a.project = change.getProject().get(); a.project = change.getProject().get();
@ -429,6 +450,19 @@ public class EventFactory {
* @param patchSet * @param patchSet
* @return object suitable for serialization to JSON * @return object suitable for serialization to JSON
*/ */
public PatchSetAttribute asPatchSetAttribute(RevWalk revWalk,
PatchSet patchSet) {
return asPatchSetAttribute(revWalk, patchSet);
}
/**
* Create a PatchSetAttribute for the given patchset suitable for
* serialization to JSON.
*
* @param db Review database
* @param patchSet
* @return object suitable for serialization to JSON
*/
public PatchSetAttribute asPatchSetAttribute(ReviewDb db, RevWalk revWalk, public PatchSetAttribute asPatchSetAttribute(ReviewDb db, RevWalk revWalk,
PatchSet patchSet) { PatchSet patchSet) {
PatchSetAttribute p = new PatchSetAttribute(); PatchSetAttribute p = new PatchSetAttribute();

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class HashtagsChangedEvent extends ChangeEvent { public class HashtagsChangedEvent extends ChangeEvent {
public AccountAttribute editor; public Supplier<AccountAttribute> editor;
public String[] added; public String[] added;
public String[] removed; public String[] removed;
public String[] hashtags; public String[] hashtags;

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class MergeFailedEvent extends PatchSetEvent { public class MergeFailedEvent extends PatchSetEvent {
public AccountAttribute submitter; public Supplier<AccountAttribute> submitter;
public String reason; public String reason;
public MergeFailedEvent() { public MergeFailedEvent() {

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class PatchSetCreatedEvent extends PatchSetEvent { public class PatchSetCreatedEvent extends PatchSetEvent {
public AccountAttribute uploader; public Supplier<AccountAttribute> uploader;
public PatchSetCreatedEvent() { public PatchSetCreatedEvent() {
super("patchset-created"); super("patchset-created");

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.PatchSetAttribute; import com.google.gerrit.server.data.PatchSetAttribute;
public class PatchSetEvent extends ChangeEvent { public class PatchSetEvent extends ChangeEvent {
public PatchSetAttribute patchSet; public Supplier<PatchSetAttribute> patchSet;
protected PatchSetEvent(String type) { protected PatchSetEvent(String type) {
super(type); super(type);

View File

@ -14,13 +14,14 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute; import com.google.gerrit.server.data.RefUpdateAttribute;
public class RefUpdatedEvent extends RefEvent { public class RefUpdatedEvent extends RefEvent {
public AccountAttribute submitter; public Supplier<AccountAttribute> submitter;
public RefUpdateAttribute refUpdate; public Supplier<RefUpdateAttribute> refUpdate;
public RefUpdatedEvent() { public RefUpdatedEvent() {
super("ref-updated"); super("ref-updated");
@ -28,11 +29,11 @@ public class RefUpdatedEvent extends RefEvent {
@Override @Override
public Project.NameKey getProjectNameKey() { public Project.NameKey getProjectNameKey() {
return new Project.NameKey(refUpdate.project); return new Project.NameKey(refUpdate.get().project);
} }
@Override @Override
public String getRefName() { public String getRefName() {
return refUpdate.refName; return refUpdate.get().refName;
} }
} }

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class ReviewerAddedEvent extends PatchSetEvent { public class ReviewerAddedEvent extends PatchSetEvent {
public AccountAttribute reviewer; public Supplier<AccountAttribute> reviewer;
public ReviewerAddedEvent() { public ReviewerAddedEvent() {
super("reviewer-added"); super("reviewer-added");

View File

@ -14,10 +14,11 @@
package com.google.gerrit.server.events; package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
import com.google.gerrit.server.data.AccountAttribute; import com.google.gerrit.server.data.AccountAttribute;
public class TopicChangedEvent extends ChangeEvent { public class TopicChangedEvent extends ChangeEvent {
public AccountAttribute changer; public Supplier<AccountAttribute> changer;
public String oldTopic; public String oldTopic;
public TopicChangedEvent() { public TopicChangedEvent() {

View File

@ -17,6 +17,7 @@ package com.google.gerrit.sshd.commands;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER; import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Supplier;
import com.google.gerrit.common.EventListener; import com.google.gerrit.common.EventListener;
import com.google.gerrit.common.EventSource; import com.google.gerrit.common.EventSource;
import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.GlobalCapability;
@ -30,12 +31,17 @@ import com.google.gerrit.sshd.BaseCommand;
import com.google.gerrit.sshd.CommandMetaData; import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.StreamCommandExecutor; import com.google.gerrit.sshd.StreamCommandExecutor;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.apache.sshd.server.Environment; import org.apache.sshd.server.Environment;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@ -63,7 +69,7 @@ final class StreamEvents extends BaseCommand {
private final LinkedBlockingQueue<Event> queue = private final LinkedBlockingQueue<Event> queue =
new LinkedBlockingQueue<>(MAX_EVENTS); new LinkedBlockingQueue<>(MAX_EVENTS);
private final Gson gson = new Gson(); private Gson gson;
/** Special event to notify clients they missed other events. */ /** Special event to notify clients they missed other events. */
private static final class DroppedOutputEvent extends Event { private static final class DroppedOutputEvent extends Event {
@ -139,6 +145,10 @@ final class StreamEvents extends BaseCommand {
stdout = toPrintWriter(out); stdout = toPrintWriter(out);
source.addEventListener(listener, currentUser); source.addEventListener(listener, currentUser);
gson = new GsonBuilder()
.registerTypeAdapter(Supplier.class, new SupplierSerializer())
.create();
} }
@Override @Override
@ -247,4 +257,13 @@ final class StreamEvents extends BaseCommand {
stdout.flush(); stdout.flush();
} }
} }
private static class SupplierSerializer
implements JsonSerializer<Supplier<?>> {
@Override
public JsonElement serialize(Supplier<?> src, Type typeOfSrc,
JsonSerializationContext context) {
return context.serialize(src.get());
}
}
} }