Merge "Switch one more CharMatcher.WHITESPACE to CharMatcher.whitespace()."
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index 2d96b84..6efabff 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -360,7 +360,7 @@
* Determine the sha1 hash of the zip file:
+
----
- openssl sha1 4.10.0-6-gd0a2dda.zip
+ openssl sha1 codemirror-4.10.0-6-gd0a2dda.zip
----
* Upload the zip file to the
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 01dfcc2..e32e30d 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -381,10 +381,16 @@
* `com.google.gerrit.common.EventListener`:
+
-Allows to listen to events. These are the same
-link:cmd-stream-events.html#events[events] that are also streamed by
+Allows to listen to events without user visibility restrictions. These
+are the same link:cmd-stream-events.html#events[events] that are also streamed by
the link:cmd-stream-events.html[gerrit stream-events] command.
+* `com.google.gerrit.common.UserScopedEventListener`:
++
+Allows to listen to events visible to the specified user. These are the
+same link:cmd-stream-events.html#events[events] that are also streamed
+by the link:cmd-stream-events.html[gerrit stream-events] command.
+
* `com.google.gerrit.extensions.events.LifecycleListener`:
+
Plugin start and stop
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index e37bde8..2b7f930 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -31,8 +31,7 @@
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
-import com.google.gerrit.common.EventListener;
-import com.google.gerrit.common.EventSource;
+import com.google.gerrit.common.UserScopedEventListener;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ProjectInput;
@@ -43,6 +42,8 @@
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.LabelInfo;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
@@ -106,9 +107,9 @@
private Submit submitHandler;
@Inject
- EventSource source;
+ DynamicSet<UserScopedEventListener> eventListeners;
- private EventListener eventListener;
+ private RegistrationHandle eventListenerRegistration;
private String systemTimeZone;
@@ -127,26 +128,30 @@
@Before
public void setUp() throws Exception {
mergeResults = Maps.newHashMap();
- CurrentUser listenerUser = factory.create(user.id);
- eventListener = new EventListener() {
- @Override
- public void onEvent(Event event) {
- if (!(event instanceof ChangeMergedEvent)) {
- return;
- }
- ChangeMergedEvent e = (ChangeMergedEvent) event;
- ChangeAttribute c = e.change.get();
- PatchSetAttribute ps = e.patchSet.get();
- log.debug("Merged {},{} as {}", ps.number, c.number, e.newRev);
- mergeResults.put(e.change.get().number, e.newRev);
- }
- };
- source.addEventListener(eventListener, listenerUser);
+ eventListenerRegistration =
+ eventListeners.add(new UserScopedEventListener() {
+ @Override
+ public void onEvent(Event event) {
+ if (!(event instanceof ChangeMergedEvent)) {
+ return;
+ }
+ ChangeMergedEvent e = (ChangeMergedEvent) event;
+ ChangeAttribute c = e.change.get();
+ PatchSetAttribute ps = e.patchSet.get();
+ log.debug("Merged {},{} as {}", ps.number, c.number, e.newRev);
+ mergeResults.put(e.change.get().number, e.newRev);
+ }
+
+ @Override
+ public CurrentUser getUser() {
+ return factory.create(user.id);
+ }
+ });
}
@After
public void cleanup() {
- source.removeEventListener(eventListener);
+ eventListenerRegistration.remove();
db.close();
}
diff --git a/gerrit-plugin-api/BUCK b/gerrit-plugin-api/BUCK
index 3ef4fdb..2a55afe 100644
--- a/gerrit-plugin-api/BUCK
+++ b/gerrit-plugin-api/BUCK
@@ -43,6 +43,7 @@
'//lib/log:api',
'//lib/mina:sshd',
'@jgit//org.eclipse.jgit:jgit',
+ '@jgit//org.eclipse.jgit.http.server:jgit-servlet',
],
visibility = ['PUBLIC'],
)
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
index ddfc8c6..bc7ebc9 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
@@ -18,6 +18,7 @@
import com.google.gwtorm.client.CompoundKey;
import java.sql.Timestamp;
+import java.util.Date;
import java.util.Objects;
/** An approval (or negative approval) on a patch set. */
@@ -95,7 +96,7 @@
protected PatchSetApproval() {
}
- public PatchSetApproval(PatchSetApproval.Key k, short v, Timestamp ts) {
+ public PatchSetApproval(PatchSetApproval.Key k, short v, Date ts) {
key = k;
setValue(v);
setGranted(ts);
@@ -136,8 +137,12 @@
return granted;
}
- public void setGranted(Timestamp ts) {
- granted = ts;
+ public void setGranted(Date when) {
+ if (when instanceof Timestamp) {
+ granted = (Timestamp) when;
+ } else {
+ granted = new Timestamp(when.getTime());
+ }
}
public String getLabel() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 410e532..400c1c0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -92,7 +92,6 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
@@ -102,7 +101,7 @@
/** Spawns local executables when a hook action occurs. */
@Singleton
public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
- EventSource, LifecycleListener, NewProjectCreatedListener {
+ LifecycleListener, NewProjectCreatedListener {
/** A logger for this class. */
private static final Logger log = LoggerFactory.getLogger(ChangeHookRunner.class);
@@ -112,22 +111,11 @@
bind(ChangeHookRunner.class);
bind(ChangeHooks.class).to(ChangeHookRunner.class);
bind(EventDispatcher.class).to(ChangeHookRunner.class);
- bind(EventSource.class).to(ChangeHookRunner.class);
DynamicSet.bind(binder(), NewProjectCreatedListener.class).to(ChangeHookRunner.class);
listener().to(ChangeHookRunner.class);
}
}
- private static class EventListenerHolder {
- final EventListener listener;
- final CurrentUser user;
-
- EventListenerHolder(EventListener l, CurrentUser u) {
- listener = l;
- user = u;
- }
- }
-
/** Container class used to hold the return code and output of script hook execution */
public static class HookResult {
private int exitValue = -1;
@@ -177,9 +165,8 @@
}
/** Listeners to receive changes as they happen (limited by visibility
- * of holder's user). */
- private final Map<EventListener, EventListenerHolder> listeners =
- new ConcurrentHashMap<>();
+ * of user). */
+ private final DynamicSet<UserScopedEventListener> listeners;
/** Listeners to receive all changes as they happen. */
private final DynamicSet<EventListener> unrestrictedListeners;
@@ -268,6 +255,7 @@
ProjectCache projectCache,
AccountCache accountCache,
EventFactory eventFactory,
+ DynamicSet<UserScopedEventListener> listeners,
DynamicSet<EventListener> unrestrictedListeners,
ChangeNotes.Factory notesFactory) {
this.anonymousCowardName = anonymousCowardName;
@@ -277,6 +265,7 @@
this.accountCache = accountCache;
this.eventFactory = eventFactory;
this.sitePaths = sitePath;
+ this.listeners = listeners;
this.unrestrictedListeners = unrestrictedListeners;
this.notesFactory = notesFactory;
@@ -319,16 +308,6 @@
return Files.exists(p) ? Optional.of(p) : Optional.<Path>absent();
}
- @Override
- public void addEventListener(EventListener listener, CurrentUser user) {
- listeners.put(listener, new EventListenerHolder(listener, user));
- }
-
- @Override
- public void removeEventListener(EventListener listener) {
- listeners.remove(listener);
- }
-
/**
* Get the Repository for the given project name, or null on error.
*
@@ -923,9 +902,9 @@
private void fireEvent(Change change, ChangeEvent event, ReviewDb db)
throws OrmException {
- for (EventListenerHolder holder : listeners.values()) {
- if (isVisibleTo(change, holder.user, db)) {
- holder.listener.onEvent(event);
+ for (UserScopedEventListener listener : listeners) {
+ if (isVisibleTo(change, listener.getUser(), db)) {
+ listener.onEvent(event);
}
}
@@ -933,9 +912,9 @@
}
private void fireEvent(Project.NameKey project, ProjectEvent event) {
- for (EventListenerHolder holder : listeners.values()) {
- if (isVisibleTo(project, holder.user)) {
- holder.listener.onEvent(event);
+ for (UserScopedEventListener listener : listeners) {
+ if (isVisibleTo(project, listener.getUser())) {
+ listener.onEvent(event);
}
}
@@ -943,9 +922,9 @@
}
private void fireEvent(Branch.NameKey branchName, RefEvent event) {
- for (EventListenerHolder holder : listeners.values()) {
- if (isVisibleTo(branchName, holder.user)) {
- holder.listener.onEvent(event);
+ for (UserScopedEventListener listener : listeners) {
+ if (isVisibleTo(branchName, listener.getUser())) {
+ listener.onEvent(event);
}
}
@@ -954,9 +933,9 @@
private void fireEvent(com.google.gerrit.server.events.Event event,
ReviewDb db) throws OrmException {
- for (EventListenerHolder holder : listeners.values()) {
- if (isVisibleTo(event, holder.user, db)) {
- holder.listener.onEvent(event);
+ for (UserScopedEventListener listener : listeners) {
+ if (isVisibleTo(event, listener.getUser(), db)) {
+ listener.onEvent(event);
}
}
@@ -999,7 +978,7 @@
RefEvent refEvent = (RefEvent) event;
String ref = refEvent.getRefName();
if (PatchSet.isChangeRef(ref)) {
- Change.Id cid= PatchSet.Id.fromRef(ref).getParentKey();
+ Change.Id cid = PatchSet.Id.fromRef(ref).getParentKey();
Change change = notesFactory
.create(db, refEvent.getProjectNameKey(), cid).getChange();
return isVisibleTo(change, user, db);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
index 633c1a4..832b3d6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
@@ -21,7 +21,6 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.events.ChangeEvent;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.ProjectEvent;
@@ -34,12 +33,7 @@
import java.util.Set;
/** Does not invoke hooks. */
-public final class DisabledChangeHooks implements ChangeHooks, EventDispatcher,
- EventSource {
- @Override
- public void addEventListener(EventListener listener, CurrentUser user) {
- }
-
+public final class DisabledChangeHooks implements ChangeHooks, EventDispatcher {
@Override
public void doChangeAbandonedHook(Change change, Account account,
PatchSet patchSet, String reason, ReviewDb db) {
@@ -106,10 +100,6 @@
}
@Override
- public void removeEventListener(EventListener listener) {
- }
-
- @Override
public HookResult doRefUpdateHook(Project project, String refName,
Account uploader, ObjectId oldId, ObjectId newId) {
return null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/EventListener.java b/gerrit-server/src/main/java/com/google/gerrit/common/EventListener.java
index 97be844..b2d5680 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/EventListener.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/EventListener.java
@@ -17,6 +17,10 @@
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.server.events.Event;
+/**
+ * Allows to listen to events without user visibility restrictions. To listen to
+ * events visible to a specific user, use {@link UserScopedEventListener}.
+ */
@ExtensionPoint
public interface EventListener {
void onEvent(Event event);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/EventSource.java b/gerrit-server/src/main/java/com/google/gerrit/common/UserScopedEventListener.java
similarity index 62%
rename from gerrit-server/src/main/java/com/google/gerrit/common/EventSource.java
rename to gerrit-server/src/main/java/com/google/gerrit/common/UserScopedEventListener.java
index bde6f5d..22435ba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/EventSource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/UserScopedEventListener.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 The Android Open Source Project
+// Copyright (C) 2016 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.
@@ -11,14 +11,16 @@
// 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.common;
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.server.CurrentUser;
-/** Distributes Events to ChangeListeners. Register listeners here. */
-public interface EventSource {
- void addEventListener(EventListener listener, CurrentUser user);
-
- void removeEventListener(EventListener listener);
+/**
+ * Allows to listen to events visible to the specified user. To listen to events
+ * without user visibility restrictions, use {@link EventListener}.
+ */
+@ExtensionPoint
+public interface UserScopedEventListener extends EventListener {
+ CurrentUser getUser();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
index 7551d5f..677f849 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -31,7 +31,6 @@
import com.google.common.collect.Ordering;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
-import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.Permission;
@@ -56,6 +55,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -216,7 +216,7 @@
for (Account.Id account : need) {
cells.add(new PatchSetApproval(
new PatchSetApproval.Key(psId, account, labelId),
- (short) 0, TimeUtil.nowTs()));
+ (short) 0, update.getWhen()));
update.putReviewer(account, REVIEWER);
}
db.patchSetApprovals().insert(cells);
@@ -229,7 +229,7 @@
if (!approvals.isEmpty()) {
checkApprovals(approvals, changeCtl);
List<PatchSetApproval> cells = new ArrayList<>(approvals.size());
- Timestamp ts = TimeUtil.nowTs();
+ Date ts = update.getWhen();
for (Map.Entry<String, Short> vote : approvals.entrySet()) {
LabelType lt = labelTypes.byLabel(vote.getKey());
cells.add(new PatchSetApproval(new PatchSetApproval.Key(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
index af3c576..058c7d5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.change;
-import com.google.common.primitives.Ints;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsPost;
import com.google.gerrit.extensions.restapi.IdString;
@@ -27,7 +26,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeFinder;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.change.QueryChanges;
import com.google.gwtorm.server.OrmException;
@@ -35,7 +33,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.List;
@Singleton
@@ -48,7 +45,6 @@
private final DynamicMap<RestView<ChangeResource>> views;
private final ChangeFinder changeFinder;
private final CreateChange createChange;
- private final ChangeIndexer changeIndexer;
@Inject
ChangesCollection(
@@ -57,15 +53,13 @@
Provider<QueryChanges> queryFactory,
DynamicMap<RestView<ChangeResource>> views,
ChangeFinder changeFinder,
- CreateChange createChange,
- ChangeIndexer changeIndexer) {
+ CreateChange createChange) {
this.db = db;
this.user = user;
this.queryFactory = queryFactory;
this.views = views;
this.changeFinder = changeFinder;
this.createChange = createChange;
- this.changeIndexer = changeIndexer;
}
@Override
@@ -81,22 +75,10 @@
@Override
public ChangeResource parse(TopLevelResource root, IdString id)
throws ResourceNotFoundException, OrmException {
- List<ChangeControl> ctls =
- changeFinder.find(id.encoded(), user.get());
- if (ctls.isEmpty()) {
- Integer changeId = Ints.tryParse(id.get());
- if (changeId != null) {
- try {
- changeIndexer.delete(new Change.Id(changeId));
- } catch (IOException e) {
- throw new ResourceNotFoundException(id.get(), e);
- }
- }
- }
+ List<ChangeControl> ctls = changeFinder.find(id.encoded(), user.get());
if (ctls.isEmpty()) {
throw new ResourceNotFoundException(id);
- }
- if (ctls.size() != 1) {
+ } else if (ctls.size() != 1) {
throw new ResourceNotFoundException("Multiple changes found for " + id);
}
@@ -111,16 +93,11 @@
throws ResourceNotFoundException, OrmException {
List<ChangeControl> ctls = changeFinder.find(id, user.get());
if (ctls.isEmpty()) {
- try {
- changeIndexer.delete(id);
- } catch (IOException e) {
- throw new ResourceNotFoundException(toIdString(id).get(), e);
- }
throw new ResourceNotFoundException(toIdString(id));
- }
- if (ctls.size() != 1) {
+ } else if (ctls.size() != 1) {
throw new ResourceNotFoundException("Multiple changes found for " + id);
}
+
ChangeControl ctl = ctls.get(0);
if (!ctl.isVisible(db.get())) {
throw new ResourceNotFoundException(toIdString(id));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index 50efbb6..513a34f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.git.BatchUpdate;
+import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -41,7 +42,6 @@
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.UpdateException;
import com.google.gerrit.server.git.validators.CommitValidators;
-import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -62,10 +62,10 @@
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.ChangeIdUtil;
import java.io.IOException;
+import java.sql.Timestamp;
import java.util.List;
import java.util.TimeZone;
@@ -83,7 +83,6 @@
private final MergeUtil.Factory mergeUtilFactory;
private final ChangeMessagesUtil changeMessagesUtil;
private final PatchSetUtil psUtil;
- private final ChangeUpdate.Factory updateFactory;
private final BatchUpdate.Factory batchUpdateFactory;
@Inject
@@ -98,7 +97,6 @@
MergeUtil.Factory mergeUtilFactory,
ChangeMessagesUtil changeMessagesUtil,
PatchSetUtil psUtil,
- ChangeUpdate.Factory updateFactory,
BatchUpdate.Factory batchUpdateFactory) {
this.db = db;
this.seq = seq;
@@ -111,7 +109,6 @@
this.mergeUtilFactory = mergeUtilFactory;
this.changeMessagesUtil = changeMessagesUtil;
this.psUtil = psUtil;
- this.updateFactory = updateFactory;
this.batchUpdateFactory = batchUpdateFactory;
}
@@ -149,9 +146,9 @@
CodeReviewCommit commitToCherryPick =
revWalk.parseCommit(ObjectId.fromString(patch.getRevision().get()));
+ Timestamp now = TimeUtil.nowTs();
PersonIdent committerIdent =
- identifiedUser.newCommitterIdent(TimeUtil.nowTs(),
- serverTimeZone);
+ identifiedUser.newCommitterIdent(now, serverTimeZone);
final ObjectId computedChangeId =
ChangeIdUtil
@@ -187,29 +184,35 @@
+ changeKey + " reside on the same branch. "
+ "Cannot create a new patch set.");
}
- if (destChanges.size() == 1) {
- // The change key exists on the destination branch. The cherry pick
- // will be added as a new patch set.
- ChangeControl destCtl = refControl.getProjectControl()
- .controlFor(destChanges.get(0).notes());
- return insertPatchSet(git, revWalk, oi, destCtl,
- cherryPickCommit, refControl, identifiedUser);
- } else {
- // Change key not found on destination branch. We can create a new
- // change.
- String newTopic = null;
- if (!Strings.isNullOrEmpty(change.getTopic())) {
- newTopic = change.getTopic() + "-" + newDest.getShortName();
+ try (BatchUpdate bu = batchUpdateFactory.create(
+ db.get(), change.getDest().getParentKey(), identifiedUser, now)) {
+ bu.setRepository(git, revWalk, oi);
+ Change.Id result;
+ if (destChanges.size() == 1) {
+ // The change key exists on the destination branch. The cherry pick
+ // will be added as a new patch set.
+ ChangeControl destCtl = refControl.getProjectControl()
+ .controlFor(destChanges.get(0).notes());
+ result = insertPatchSet(
+ bu, git, destCtl, cherryPickCommit, refControl);
+ } else {
+ // Change key not found on destination branch. We can create a new
+ // change.
+ String newTopic = null;
+ if (!Strings.isNullOrEmpty(change.getTopic())) {
+ newTopic = change.getTopic() + "-" + newDest.getShortName();
+ }
+ result =
+ createNewChange(bu, cherryPickCommit,
+ refControl.getRefName(), newTopic, change.getDest());
+
+ bu.addOp(change.getId(),
+ new AddMessageToSourceChangeOp(
+ changeMessagesUtil, patch.getId(), destinationBranch,
+ cherryPickCommit));
}
- Change.Id newChangeId =
- createNewChange(git, revWalk, oi, project, cherryPickCommit,
- refControl.getRefName(), identifiedUser, newTopic,
- change.getDest());
-
- addMessageToSourceChange(change, patch.getId(), destinationBranch,
- cherryPickCommit, identifiedUser, refControl);
-
- return newChangeId;
+ bu.execute();
+ return result;
}
} catch (MergeIdenticalTreeException | MergeConflictException e) {
throw new IntegrationException("Cherry pick failed: " + e.getMessage());
@@ -219,10 +222,9 @@
}
}
- private Change.Id insertPatchSet(Repository git, RevWalk revWalk,
- ObjectInserter oi, ChangeControl ctl, CodeReviewCommit cherryPickCommit,
- RefControl refControl, IdentifiedUser identifiedUser)
- throws IOException, OrmException, UpdateException, RestApiException {
+ private Change.Id insertPatchSet(BatchUpdate bu, Repository git,
+ ChangeControl ctl, CodeReviewCommit cherryPickCommit,
+ RefControl refControl) throws IOException, OrmException {
Change change = ctl.getChange();
PatchSet.Id psId =
ChangeUtil.nextPatchSetId(git, change.currentPatchSetId());
@@ -231,24 +233,16 @@
PatchSet.Id newPatchSetId = inserter.getPatchSetId();
PatchSet current = psUtil.current(db.get(), ctl.getNotes());
- try (BatchUpdate bu = batchUpdateFactory.create(
- db.get(), change.getDest().getParentKey(), identifiedUser,
- TimeUtil.nowTs())) {
- bu.setRepository(git, revWalk, oi);
- bu.addOp(change.getId(), inserter
- .setMessage("Uploaded patch set " + newPatchSetId.get() + ".")
- .setDraft(current.isDraft())
- .setSendMail(false));
- bu.execute();
- }
+ bu.addOp(change.getId(), inserter
+ .setMessage("Uploaded patch set " + newPatchSetId.get() + ".")
+ .setDraft(current.isDraft())
+ .setSendMail(false));
return change.getId();
}
- private Change.Id createNewChange(Repository git, RevWalk revWalk,
- ObjectInserter oi, Project.NameKey project,
- CodeReviewCommit cherryPickCommit, String refName,
- IdentifiedUser identifiedUser, String topic, Branch.NameKey sourceBranch)
- throws RestApiException, UpdateException, OrmException {
+ private Change.Id createNewChange(BatchUpdate bu,
+ CodeReviewCommit cherryPickCommit, String refName, String topic,
+ Branch.NameKey sourceBranch) throws OrmException {
Change.Id changeId = new Change.Id(seq.nextChangeId());
ChangeInserter ins = changeInserterFactory.create(
changeId, cherryPickCommit, refName)
@@ -257,38 +251,44 @@
ins.setMessage(
messageForDestinationChange(ins.getPatchSetId(), sourceBranch));
- try (BatchUpdate bu = batchUpdateFactory.create(
- db.get(), project, identifiedUser, TimeUtil.nowTs())) {
- bu.setRepository(git, revWalk, oi);
- bu.insertChange(ins);
- bu.execute();
- }
+ bu.insertChange(ins);
return changeId;
}
- private void addMessageToSourceChange(Change change, PatchSet.Id patchSetId,
- String destinationBranch, CodeReviewCommit cherryPickCommit,
- IdentifiedUser identifiedUser, RefControl refControl)
- throws OrmException, IOException {
- ChangeMessage changeMessage = new ChangeMessage(
- new ChangeMessage.Key(
- patchSetId.getParentKey(), ChangeUtil.messageUUID(db.get())),
- identifiedUser.getAccountId(), TimeUtil.nowTs(), patchSetId);
- StringBuilder sb = new StringBuilder("Patch Set ")
- .append(patchSetId.get())
- .append(": Cherry Picked")
- .append("\n\n")
- .append("This patchset was cherry picked to branch ")
- .append(destinationBranch)
- .append(" as commit ")
- .append(cherryPickCommit.getId().getName());
- changeMessage.setMessage(sb.toString());
+ private static class AddMessageToSourceChangeOp extends BatchUpdate.Op {
+ private final ChangeMessagesUtil cmUtil;
+ private final PatchSet.Id psId;
+ private final String destBranch;
+ private final ObjectId cherryPickCommit;
- ChangeControl ctl = refControl.getProjectControl()
- .controlFor(db.get(), change);
- ChangeUpdate update = updateFactory.create(ctl, TimeUtil.nowTs());
- changeMessagesUtil.addChangeMessage(db.get(), update, changeMessage);
- update.commit();
+ private AddMessageToSourceChangeOp(ChangeMessagesUtil cmUtil,
+ PatchSet.Id psId, String destBranch, ObjectId cherryPickCommit) {
+ this.cmUtil = cmUtil;
+ this.psId = psId;
+ this.destBranch = destBranch;
+ this.cherryPickCommit = cherryPickCommit;
+ }
+
+ @Override
+ public boolean updateChange(ChangeContext ctx) throws OrmException {
+ ChangeMessage changeMessage = new ChangeMessage(
+ new ChangeMessage.Key(
+ ctx.getChange().getId(), ChangeUtil.messageUUID(ctx.getDb())),
+ ctx.getUser().getAccountId(), ctx.getWhen(), psId);
+ StringBuilder sb = new StringBuilder("Patch Set ")
+ .append(psId.get())
+ .append(": Cherry Picked")
+ .append("\n\n")
+ .append("This patchset was cherry picked to branch ")
+ .append(destBranch)
+ .append(" as commit ")
+ .append(cherryPickCommit.name());
+ changeMessage.setMessage(sb.toString());
+
+ cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), changeMessage);
+ ctx.saveChange(); // Bump lastUpdatedOn to match message.
+ return true;
+ }
}
private String messageForDestinationChange(PatchSet.Id patchSetId,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index 37ca9cf..dc3b26b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -124,7 +124,7 @@
new ChangeMessage(new ChangeMessage.Key(ctx.getChange().getId(),
ChangeUtil.messageUUID(ctx.getDb())),
ctx.getUser().getAccountId(),
- TimeUtil.nowTs(), currPs);
+ ctx.getWhen(), currPs);
changeMessage.setMessage(msg.toString());
cmUtil.addChangeMessage(ctx.getDb(), update, changeMessage);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index d1cc992..bf40bbb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -553,7 +553,7 @@
psId,
user.getAccountId(),
lt.getLabelId()),
- ent.getValue(), TimeUtil.nowTs());
+ ent.getValue(), ctx.getWhen());
c.setGranted(ctx.getWhen());
ups.add(c);
addLabelDelta(normName, c.getValue());
@@ -587,7 +587,7 @@
user.getAccountId(),
ctx.getControl().getLabelTypes().getLabelTypes().get(0)
.getLabelId()),
- (short) 0, TimeUtil.nowTs());
+ (short) 0, ctx.getWhen());
c.setGranted(ctx.getWhen());
ups.add(c);
} else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
index 0707dc7..8a1f290 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
@@ -43,6 +43,7 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.sql.Timestamp;
import java.util.Collections;
@Singleton
@@ -143,20 +144,21 @@
comment.getParentUuid(), ctx.getWhen());
setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
plcUtil.putComments(ctx.getDb(), update,
- Collections.singleton(update(comment, in)));
+ Collections.singleton(update(comment, in, ctx.getWhen())));
} else {
if (comment.getRevId() == null) {
setCommentRevId(
comment, patchListCache, ctx.getChange(), ps);
}
plcUtil.putComments(ctx.getDb(), update,
- Collections.singleton(update(comment, in)));
+ Collections.singleton(update(comment, in, ctx.getWhen())));
}
return true;
}
}
- private static PatchLineComment update(PatchLineComment e, DraftInput in) {
+ private static PatchLineComment update(PatchLineComment e, DraftInput in,
+ Timestamp when) {
if (in.side != null) {
e.setSide(in.side == Side.PARENT ? (short) 0 : (short) 1);
}
@@ -168,7 +170,7 @@
e.setRange(in.range);
e.setLine(in.range != null ? in.range.endLine : in.line);
}
- e.setWrittenOn(TimeUtil.nowTs());
+ e.setWrittenOn(when);
return e;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index b5c8bb7..7d9bd15 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -19,6 +19,7 @@
import com.google.common.cache.Cache;
import com.google.gerrit.audit.AuditModule;
import com.google.gerrit.common.EventListener;
+import com.google.gerrit.common.UserScopedEventListener;
import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider;
import com.google.gerrit.extensions.config.CapabilityDefinition;
import com.google.gerrit.extensions.config.CloneCommand;
@@ -281,6 +282,7 @@
.to(ProjectConfigEntry.UpdateChecker.class);
DynamicSet.setOf(binder(), EventListener.class);
DynamicSet.bind(binder(), EventListener.class).to(EventsMetrics.class);
+ DynamicSet.setOf(binder(), UserScopedEventListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), RefOperationValidationListener.class);
DynamicSet.setOf(binder(), MergeValidationListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index e44c810..ca4ac04 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -67,6 +67,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.sql.Timestamp;
import java.util.Map;
import java.util.TimeZone;
@@ -136,8 +137,8 @@
ObjectId revision = ObjectId.fromString(ps.getRevision().get());
String editRefName = RefNames.refsEdit(me.getAccountId(), change.getId(),
ps.getId());
- Result res =
- update(repo, me, editRefName, rw, ObjectId.zeroId(), revision);
+ Result res = update(repo, me, editRefName, rw, ObjectId.zeroId(),
+ revision, TimeUtil.nowTs());
indexer.index(reviewDb.get(), change);
return res;
}
@@ -244,11 +245,12 @@
RevWalk rw = new RevWalk(repo);
ObjectInserter inserter = repo.newObjectInserter()) {
String refName = edit.getRefName();
+ Timestamp now = TimeUtil.nowTs();
ObjectId commit = createCommit(me, inserter, prevEdit,
prevEdit.getTree(),
- msg);
+ msg, now);
inserter.flush();
- return update(repo, me, refName, rw, prevEdit, commit);
+ return update(repo, me, refName, rw, prevEdit, commit, now);
}
}
@@ -344,9 +346,10 @@
throw new InvalidChangeOperationException("no changes were made");
}
- ObjectId commit = createCommit(me, inserter, prevEdit, newTree);
+ Timestamp now = TimeUtil.nowTs();
+ ObjectId commit = createCommit(me, inserter, prevEdit, newTree, now);
inserter.flush();
- return update(repo, me, refName, rw, prevEdit, commit);
+ return update(repo, me, refName, rw, prevEdit, commit, now);
}
}
@@ -365,30 +368,30 @@
}
private ObjectId createCommit(IdentifiedUser me, ObjectInserter inserter,
- RevCommit revision, ObjectId tree) throws IOException {
+ RevCommit revision, ObjectId tree, Timestamp when) throws IOException {
return createCommit(me, inserter, revision, tree,
- revision.getFullMessage());
+ revision.getFullMessage(), when);
}
private ObjectId createCommit(IdentifiedUser me, ObjectInserter inserter,
- RevCommit revision, ObjectId tree, String msg)
+ RevCommit revision, ObjectId tree, String msg, Timestamp when)
throws IOException {
CommitBuilder builder = new CommitBuilder();
builder.setTreeId(tree);
builder.setParentIds(revision.getParents());
builder.setAuthor(revision.getAuthorIdent());
- builder.setCommitter(getCommitterIdent(me));
+ builder.setCommitter(getCommitterIdent(me, when));
builder.setMessage(msg);
return inserter.insert(builder);
}
private RefUpdate.Result update(Repository repo, IdentifiedUser me,
- String refName, RevWalk rw, ObjectId oldObjectId, ObjectId newEdit)
- throws IOException {
+ String refName, RevWalk rw, ObjectId oldObjectId, ObjectId newEdit,
+ Timestamp when) throws IOException {
RefUpdate ru = repo.updateRef(refName);
ru.setExpectedOldObjectId(oldObjectId);
ru.setNewObjectId(newEdit);
- ru.setRefLogIdent(getRefLogIdent(me));
+ ru.setRefLogIdent(getRefLogIdent(me, when));
ru.setRefLogMessage("inline edit (amend)", false);
ru.setForceUpdate(true);
RefUpdate.Result res = ru.update(rw);
@@ -476,11 +479,11 @@
return dc;
}
- private PersonIdent getCommitterIdent(IdentifiedUser user) {
- return user.newCommitterIdent(TimeUtil.nowTs(), tz);
+ private PersonIdent getCommitterIdent(IdentifiedUser user, Timestamp when) {
+ return user.newCommitterIdent(when, tz);
}
- private PersonIdent getRefLogIdent(IdentifiedUser user) {
- return user.newRefLogIdent(TimeUtil.nowTs(), tz);
+ private PersonIdent getRefLogIdent(IdentifiedUser user, Timestamp when) {
+ return user.newRefLogIdent(when, tz);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index a79d862..e213927 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -900,7 +900,7 @@
try {
for (ChangeData cd : internalChangeQuery.byProjectOpen(destProject)) {
try (BatchUpdate bu = batchUpdateFactory.create(db, destProject,
- internalUserFactory.create(), TimeUtil.nowTs())) {
+ internalUserFactory.create(), ts)) {
bu.addOp(cd.getId(), new BatchUpdate.Op() {
@Override
public boolean updateChange(ChangeContext ctx) throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
index a0860c7..c96ed65 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
@@ -184,15 +184,6 @@
: Futures.<Object, IOException> immediateCheckedFuture(null);
}
- /**
- * Synchronously delete a change.
- *
- * @param id change ID to delete.
- */
- public void delete(Change.Id id) throws IOException {
- new DeleteTask(id).call();
- }
-
private Collection<ChangeIndex> getWriteIndexes() {
return indexes != null
? indexes.getWriteIndexes()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 6d03df28..aded275 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -384,6 +384,9 @@
private RevisionNoteMap getRevisionNoteMap(RevWalk rw, ObjectId curr)
throws ConfigInvalidException, OrmException, IOException {
+ if (curr.equals(ObjectId.zeroId())) {
+ return RevisionNoteMap.emptyMap();
+ }
if (migration.readChanges()) {
// If reading from changes is enabled, then the old ChangeNotes already
// parsed the revision notes. We can reuse them as long as the ref hasn't
@@ -394,12 +397,7 @@
return checkNotNull(ctl.getNotes().revisionNoteMap);
}
}
- NoteMap noteMap;
- if (!curr.equals(ObjectId.zeroId())) {
- noteMap = NoteMap.read(rw.getObjectReader(), rw.parseCommit(curr));
- } else {
- noteMap = NoteMap.newEmptyMap();
- }
+ NoteMap noteMap = NoteMap.read(rw.getObjectReader(), rw.parseCommit(curr));
// Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteMap.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
index 17f33b0..a804d2b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
@@ -43,6 +43,11 @@
return new RevisionNoteMap(noteMap, ImmutableMap.copyOf(result));
}
+ static RevisionNoteMap emptyMap() {
+ return new RevisionNoteMap(NoteMap.newEmptyMap(),
+ ImmutableMap.<RevId, RevisionNote> of());
+ }
+
private RevisionNoteMap(NoteMap noteMap,
ImmutableMap<RevId, RevisionNote> revisionNotes) {
this.noteMap = noteMap;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 82018fb..312ac89 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -541,6 +541,12 @@
gApi.changes().id(change.getId().get()).current()
.review(new ReviewInput().label("Code-Review", 1));
+ Map<String, Short> m = gApi.changes()
+ .id(change.getId().get())
+ .reviewer(user.getAccountId().toString())
+ .votes();
+ assertThat(m).hasSize(1);
+ assertThat(m).containsEntry("Code-Review", new Short((short)1));
assertQuery("label:Code-Review=-2");
assertQuery("label:Code-Review-2");
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
index bead9b8..29b7987 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -18,10 +18,12 @@
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.EventSource;
+import com.google.gerrit.common.UserScopedEventListener;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventTypes;
@@ -63,7 +65,7 @@
private IdentifiedUser currentUser;
@Inject
- private EventSource source;
+ private DynamicSet<UserScopedEventListener> eventListeners;
@Inject
@StreamCommandExecutor
@@ -75,6 +77,8 @@
private Gson gson;
+ private RegistrationHandle eventListenerRegistration;
+
/** Special event to notify clients they missed other events. */
private static final class DroppedOutputEvent extends Event {
private final static String TYPE = "dropped-output";
@@ -87,16 +91,6 @@
EventTypes.register(DroppedOutputEvent.TYPE, DroppedOutputEvent.class);
}
- private final EventListener listener = new EventListener() {
- @Override
- public void onEvent(final Event event) {
- if (subscribedToEvents.isEmpty()
- || subscribedToEvents.contains(event.getType())) {
- offer(event);
- }
- }
- };
-
private final CancelableRunnable writer = new CancelableRunnable() {
@Override
public void run() {
@@ -150,7 +144,21 @@
}
stdout = toPrintWriter(out);
- source.addEventListener(listener, currentUser);
+ eventListenerRegistration =
+ eventListeners.add(new UserScopedEventListener() {
+ @Override
+ public void onEvent(final Event event) {
+ if (subscribedToEvents.isEmpty()
+ || subscribedToEvents.contains(event.getType())) {
+ offer(event);
+ }
+ }
+
+ @Override
+ public CurrentUser getUser() {
+ return currentUser;
+ }
+ });
gson = new GsonBuilder()
.registerTypeAdapter(Supplier.class, new SupplierSerializer())
@@ -159,7 +167,7 @@
@Override
protected void onExit(final int rc) {
- source.removeEventListener(listener);
+ eventListenerRegistration.remove();
synchronized (taskLock) {
done = true;
@@ -170,7 +178,7 @@
@Override
public void destroy() {
- source.removeEventListener(listener);
+ eventListenerRegistration.remove();
final boolean exit;
synchronized (taskLock) {
@@ -218,7 +226,7 @@
// destroy() above, or it closed the stream and is no longer
// accepting output. Either way terminate this instance.
//
- source.removeEventListener(listener);
+ eventListenerRegistration.remove();
flush();
onExit(0);
return;
diff --git a/lib/BUCK b/lib/BUCK
index 8bf12af..f21f207 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -61,8 +61,8 @@
maven_jar(
name = 'guava',
- id = 'com.google.guava:guava:19.0-rc2',
- sha1 = '93e17f60bc524c2610b41c494bb829c11ca89436',
+ id = 'com.google.guava:guava:19.0',
+ sha1 = '6ce200f6b23222af3d8abb6b6459e6c44f4bb0e9',
license = 'Apache2.0',
)
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs
index 9677058..f970048 100644
--- a/lib/codemirror/cm.defs
+++ b/lib/codemirror/cm.defs
@@ -41,6 +41,7 @@
# Available modes must be enumerated here,
# in gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java,
+# gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java,
# and in CodeMirror's own mode/meta.js script.
CM_MODES = [
'clike',
diff --git a/polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown.html
rename to polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
index 7615d15..0d549d9 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
@@ -16,7 +16,7 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
-<link rel="import" href="../gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
<dom-module id="gr-account-dropdown">
<style>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
similarity index 100%
rename from polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown.js
rename to polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
diff --git a/polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown_test.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
similarity index 100%
rename from polygerrit-ui/app/elements/shared/gr-account-dropdown/gr-account-dropdown_test.html
rename to polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
diff --git a/polygerrit-ui/app/elements/shared/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
similarity index 98%
rename from polygerrit-ui/app/elements/shared/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
rename to polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
index 9988c28..a47c50f 100644
--- a/polygerrit-ui/app/elements/shared/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -15,7 +15,7 @@
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
<dom-module id="gr-keyboard-shortcuts-dialog">
<template>
diff --git a/polygerrit-ui/app/elements/shared/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
similarity index 100%
rename from polygerrit-ui/app/elements/shared/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
rename to polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
new file mode 100644
index 0000000..129a321
--- /dev/null
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -0,0 +1,87 @@
+<!--
+Copyright (C) 2016 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+
+<link rel="import" href="../gr-account-dropdown/gr-account-dropdown.html">
+<link rel="import" href="../gr-search-bar/gr-search-bar.html">
+
+<dom-module id="gr-main-header">
+ <template>
+ <style>
+ :host {
+ align-items: center;
+ display: flex;
+ overflow: hidden;
+ }
+ .bigTitle {
+ color: var(--primary-text-color);
+ font-size: 1.75em;
+ text-decoration: none;
+ }
+ .bigTitle:hover {
+ text-decoration: underline;
+ }
+ .rightItems {
+ display: flex;
+ flex: 1;
+ justify-content: flex-end;
+ }
+ gr-search-bar {
+ margin-left: .5em;
+ width: 500px;
+ }
+ .accountContainer:not(.loggedIn):not(.loggedOut) .loginButton,
+ .accountContainer:not(.loggedIn):not(.loggedOut) gr-account-dropdown,
+ .accountContainer.loggedIn .loginButton,
+ .accountContainer.loggedOut gr-account-dropdown {
+ display: none;
+ }
+ .accountContainer {
+ align-items: center;
+ display: flex;
+ margin-left: var(--default-horizontal-margin);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ @media screen and (max-width: 50em) {
+ .bigTitle {
+ font-size: 14px;
+ font-weight: bold;
+ }
+ gr-search-bar {
+ display: none;
+ }
+ .accountContainer {
+ margin-left: .5em !important;
+ }
+ }
+ </style>
+ <a href="/" class="bigTitle">PolyGerrit</a>
+ <div class="rightItems">
+ <gr-search-bar value="{{params.query}}" role="search"></gr-search-bar>
+ <div class="accountContainer" id="accountContainer">
+ <a class="loginButton" href="/login" on-tap="_loginTapHandler">Login</a>
+ <gr-account-dropdown account="[[account]]"></gr-account-dropdown>
+ </div>
+ </div>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+ <script src="gr-main-header.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
new file mode 100644
index 0000000..ca57625
--- /dev/null
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2016 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.
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-main-header',
+
+ hostAttributes: {
+ role: 'banner'
+ },
+
+ properties: {
+ },
+
+ attached: function() {
+ this.$.restAPI.getAccount().then(function(account) {
+ var loggedIn = !!account;
+ this.$.accountContainer.classList.toggle('loggedIn', loggedIn);
+ this.$.accountContainer.classList.toggle('loggedOut', !loggedIn);
+ }.bind(this));
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
similarity index 95%
rename from polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar.html
rename to polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
index 900245b..c8c4523 100644
--- a/polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
@@ -17,7 +17,7 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior.html">
-<link rel="import" href="../gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
<dom-module id="gr-search-bar">
<template>
diff --git a/polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
similarity index 100%
rename from polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar.js
rename to polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
diff --git a/polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar_test.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
similarity index 100%
rename from polygerrit-ui/app/elements/shared/gr-search-bar/gr-search-bar_test.html
rename to polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index e7fa31c..fd6525d 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -18,16 +18,17 @@
<link rel="import" href="../behaviors/keyboard-shortcut-behavior.html">
<link rel="import" href="../styles/app-theme.html">
+<link rel="import" href="./core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html">
+<link rel="import" href="./core/gr-main-header/gr-main-header.html">
+
<link rel="import" href="./change-list/gr-change-list-view/gr-change-list-view.html">
<link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
<link rel="import" href="./change/gr-change-view/gr-change-view.html">
<link rel="import" href="./diff/gr-diff-view/gr-diff-view.html">
-<link rel="import" href="./shared/gr-account-dropdown/gr-account-dropdown.html">
<link rel="import" href="./shared/gr-ajax/gr-ajax.html">
-<link rel="import" href="./shared/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html">
<link rel="import" href="./shared/gr-overlay/gr-overlay.html">
-<link rel="import" href="./shared/gr-search-bar/gr-search-bar.html">
+<link rel="import" href="./shared/gr-rest-api-interface/gr-rest-api-interface.html">
<script src="../bower_components/page/page.js"></script>
<script src="../scripts/app.js"></script>
@@ -42,83 +43,25 @@
min-height: 100vh;
flex-direction: column;
}
- header,
+ gr-main-header,
footer {
background-color: var(--primary-color);
color: var(--primary-text-color);
padding: .5rem var(--default-horizontal-margin);
}
- header {
- align-items: center;
- display: flex;
- overflow: hidden;
- }
main {
flex: 1;
}
- .bigTitle {
- color: var(--primary-text-color);
- font-size: 1.75em;
- text-decoration: none;
- }
- .bigTitle:hover {
- text-decoration: underline;
- }
- .headerRightItems {
- display: flex;
- flex: 1;
- justify-content: flex-end;
- }
- gr-search-bar {
- margin-left: .5em;
- width: 500px;
- }
- .accountContainer:not(.loggedIn):not(.loggedOut) .loginButton,
- .accountContainer:not(.loggedIn):not(.loggedOut) gr-account-dropdown,
- .accountContainer.loggedIn .loginButton,
- .accountContainer.loggedOut gr-account-dropdown {
- display: none;
- }
- .accountContainer {
- align-items: center;
- display: flex;
- margin-left: var(--default-horizontal-margin);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
.feedback {
color: #b71c1c;
}
- @media screen and (max-width: 50em) {
- .bigTitle {
- font-size: 14px;
- font-weight: bold;
- }
- gr-search-bar {
- display: none;
- }
- .accountContainer {
- margin-left: .5em !important;
- }
- }
</style>
- <gr-ajax auto url="/accounts/self/detail" last-response="{{account}}"></gr-ajax>
<gr-ajax auto url="/config/server/info" last-response="{{config}}"></gr-ajax>
<gr-ajax auto url="/config/server/version" last-response="{{version}}"></gr-ajax>
<gr-ajax id="diffPreferencesXHR"
url="/accounts/self/preferences.diff"
last-response="{{_diffPreferences}}"></gr-ajax>
- <header role="banner">
- <a href="/" class="bigTitle">PolyGerrit</a>
- <div class="headerRightItems">
- <gr-search-bar value="{{params.query}}" role="search"></gr-search-bar>
- <div class="accountContainer" id="accountContainer">
- <a class="loginButton" href="/login" on-tap="_loginTapHandler">Login</a>
- <gr-account-dropdown account="[[account]]"></gr-account-dropdown>
- </div>
- </div>
- </header>
+ <gr-main-header></gr-main-header>
<main>
<template is="dom-if" if="{{_showChangeListView}}" restamp="true">
<gr-change-list-view
@@ -167,6 +110,7 @@
view="[[params.view]]"
on-close="_handleKeyboardShortcutDialogClose"></gr-keyboard-shortcuts-dialog>
</gr-overlay>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-app.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 23ee6c6..475ea0d 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -77,6 +77,12 @@
return !!(this.account && Object.keys(this.account).length > 0);
},
+ attached: function() {
+ this.$.restAPI.getAccount().then(function(account) {
+ this.account = account;
+ }.bind(this));
+ },
+
ready: function() {
this._viewState = {
changeView: {
@@ -98,8 +104,6 @@
_accountChanged: function() {
this._resolveAccountReady();
- this.$.accountContainer.classList.toggle('loggedIn', this.loggedIn);
- this.$.accountContainer.classList.toggle('loggedOut', !this.loggedIn);
if (this.loggedIn) {
this.$.diffPreferencesXHR.generateRequest();
} else {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 7367439..f69f8b1 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -20,6 +20,17 @@
Polymer({
is: 'gr-rest-api-interface',
+ properties: {
+ _cache: {
+ type: Object,
+ value: {}, // Intentional to share the object accross instances.
+ },
+ _sharedFetchPromises: {
+ type: Object,
+ value: {}, // Intentional to share the object accross instances.
+ },
+ },
+
fetchJSON: function(url, opt_cancelCondition, opt_params, opt_opts) {
opt_opts = opt_opts || {};
@@ -52,7 +63,13 @@
}
return response.text().then(function(text) {
- return JSON.parse(text.substring(JSON_PREFIX.length));
+ var result;
+ try {
+ result = JSON.parse(text.substring(JSON_PREFIX.length));
+ } catch (_) {
+ result = null;
+ }
+ return result;
});
}).catch(function(err) {
if (opt_opts.noCredentials) {
@@ -65,14 +82,35 @@
}.bind(this));
},
- getAccountDetail: function() {
- return this.fetchJSON('/accounts/self/detail');
+ getAccount: function() {
+ return this._fetchSharedCacheURL('/accounts/self/detail');
+ },
+
+ _fetchSharedCacheURL: function(url) {
+ if (this._sharedFetchPromises[url]) {
+ return this._sharedFetchPromises[url];
+ }
+ // TODO(andybons): Periodic cache invalidation.
+ if (this._cache[url] !== undefined) {
+ return this._cache[url];
+ }
+ this._sharedFetchPromises[url] = this.fetchJSON(url).then(
+ function(response) {
+ if (response !== undefined) {
+ this._cache[url] = response;
+ }
+ this._sharedFetchPromises[url] = undefined;
+ return response;
+ }.bind(this)).catch(function(err) {
+ this._sharedFetchPromises[url] = undefined;
+ throw err;
+ });
+ return this._sharedFetchPromises[url];
},
getDiff: function(changeNum, basePatchNum, patchNum, path,
opt_cancelCondition) {
- var url = this._changeBaseURL(changeNum, patchNum) + '/files/' +
- encodeURIComponent(path) + '/diff';
+ var url = this._getDiffFetchURL(changeNum, patchNum, path);
var params = {
context: 'ALL',
intraline: null
@@ -84,6 +122,11 @@
return this.fetchJSON(url, opt_cancelCondition, params);
},
+ _getDiffFetchURL: function(changeNum, patchNum, path) {
+ return this._changeBaseURL(changeNum, patchNum) + '/files/' +
+ encodeURIComponent(path) + '/diff';
+ },
+
getDiffComments: function(changeNum, basePatchNum, patchNum, path) {
return this._getDiffComments(changeNum, basePatchNum, patchNum, path,
'/comments');
@@ -102,7 +145,7 @@
var promises = [];
var comments;
var baseComments;
- var url = this._getDiffFetchURL(changeNum, patchNum, endpoint);
+ var url = this._getDiffCommentsFetchURL(changeNum, patchNum, endpoint);
promises.push(this.fetchJSON(url).then(function(response) {
comments = response[path] || [];
if (basePatchNum == PARENT_PATCH_NUM) {
@@ -112,7 +155,8 @@
}.bind(this)));
if (basePatchNum != PARENT_PATCH_NUM) {
- var baseURL = this._getDiffFetchURL(changeNum, basePatchNum, endpoint);
+ var baseURL = this._getDiffCommentsFetchURL(changeNum, basePatchNum,
+ endpoint);
promises.push(this.fetchJSON(baseURL).then(function(response) {
baseComments = (response[path] || []).filter(withoutParent);
}));
@@ -126,7 +170,7 @@
});
},
- _getDiffFetchURL: function(changeNum, patchNum, endpoint) {
+ _getDiffCommentsFetchURL: function(changeNum, patchNum, endpoint) {
return this._changeBaseURL(changeNum, patchNum) + endpoint;
},
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index 79c175d..c397c8f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -53,6 +53,23 @@
});
});
+ test('cached results', function(done) {
+ var n = 0;
+ var fetchJSONStub = sinon.stub(element, 'fetchJSON', function() {
+ return Promise.resolve(++n);
+ });
+ var promises = [];
+ promises.push(element._fetchSharedCacheURL('/foo'));
+ promises.push(element._fetchSharedCacheURL('/foo'));
+ promises.push(element._fetchSharedCacheURL('/foo'));
+
+ Promise.all(promises).then(function(results) {
+ assert.deepEqual(results, [1, 1, 1]);
+ fetchJSONStub.restore();
+ done();
+ });
+ });
+
test('params are properly encoded', function() {
var fetchStub = sinon.stub(window, 'fetch', function() {
return Promise.resolve({ text: function() {
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index fcfaaa3..31d93b7 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -37,6 +37,8 @@
'../elements/change/gr-reviewer-list/gr-reviewer-list_test.html',
'../elements/change-list/gr-change-list/gr-change-list_test.html',
'../elements/change-list/gr-change-list-item/gr-change-list-item_test.html',
+ '../elements/core/gr-account-dropdown/gr-account-dropdown_test.html',
+ '../elements/core/gr-search-bar/gr-search-bar_test.html',
'../elements/diff/gr-diff/gr-diff_test.html',
'../elements/diff/gr-diff-comment/gr-diff-comment_test.html',
'../elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html',
@@ -44,7 +46,6 @@
'../elements/diff/gr-diff-side/gr-diff-side_test.html',
'../elements/diff/gr-diff-view/gr-diff-view_test.html',
'../elements/diff/gr-patch-range-select/gr-patch-range-select_test.html',
- '../elements/shared/gr-account-dropdown/gr-account-dropdown_test.html',
'../elements/shared/gr-account-label/gr-account-label_test.html',
'../elements/shared/gr-account-link/gr-account-link_test.html',
'../elements/shared/gr-avatar/gr-avatar_test.html',
@@ -53,7 +54,6 @@
'../elements/shared/gr-date-formatter/gr-date-formatter_test.html',
'../elements/shared/gr-linked-text/gr-linked-text_test.html',
'../elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
- '../elements/shared/gr-search-bar/gr-search-bar_test.html',
].forEach(function(file) {
testFiles.push(file);
testFiles.push(file + '?dom=shadow');