
This reverts commit ce4e7c6609
.
Reason for revert:
The code has threading issues. For instance, we see an NPE for submitter in SubmitStrategyOp#asyncPostUpdate on real servers. Looking at the code, there doesn't seem to be any precautions for the use of the existing objects on different threads. Objects like SubmitStrategyOp are not built to be thread-safe at the moment. We can't simply pass them to another thread and expect them to work correctly.
Change-Id: If2e8c7c4d40231fdc9390f52c883401a1c0710cb
157 lines
6.0 KiB
Java
157 lines
6.0 KiB
Java
// Copyright (C) 2020 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package com.google.gerrit.server;
|
|
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.gerrit.entities.ChangeMessage;
|
|
import com.google.gerrit.entities.HumanComment;
|
|
import com.google.gerrit.entities.PatchSet;
|
|
import com.google.gerrit.entities.Project;
|
|
import com.google.gerrit.exceptions.StorageException;
|
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
|
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
|
import com.google.gerrit.server.change.EmailReviewComments;
|
|
import com.google.gerrit.server.change.NotifyResolver;
|
|
import com.google.gerrit.server.extensions.events.CommentAdded;
|
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
|
import com.google.gerrit.server.patch.PatchListNotAvailableException;
|
|
import com.google.gerrit.server.update.BatchUpdateOp;
|
|
import com.google.gerrit.server.update.ChangeContext;
|
|
import com.google.gerrit.server.update.CommentsRejectedException;
|
|
import com.google.gerrit.server.update.Context;
|
|
import com.google.gerrit.server.update.RepoView;
|
|
import com.google.gerrit.server.util.LabelVote;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.assistedinject.Assisted;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* A {@link BatchUpdateOp} that can be used to publish draft comments
|
|
*
|
|
* <p>This class uses the {@link PublishCommentUtil} to publish draft comments and fires the
|
|
* necessary event for this.
|
|
*/
|
|
public class PublishCommentsOp implements BatchUpdateOp {
|
|
private final PatchSetUtil psUtil;
|
|
private final ChangeNotes.Factory changeNotesFactory;
|
|
private final ChangeMessagesUtil cmUtil;
|
|
private final CommentAdded commentAdded;
|
|
private final CommentsUtil commentsUtil;
|
|
private final EmailReviewComments.Factory email;
|
|
private final List<LabelVote> labelDelta = new ArrayList<>();
|
|
private final Project.NameKey projectNameKey;
|
|
private final PatchSet.Id psId;
|
|
private final PublishCommentUtil publishCommentUtil;
|
|
|
|
private List<HumanComment> comments = new ArrayList<>();
|
|
private ChangeMessage message;
|
|
private IdentifiedUser user;
|
|
|
|
public interface Factory {
|
|
PublishCommentsOp create(PatchSet.Id psId, Project.NameKey projectNameKey);
|
|
}
|
|
|
|
@Inject
|
|
public PublishCommentsOp(
|
|
ChangeNotes.Factory changeNotesFactory,
|
|
ChangeMessagesUtil cmUtil,
|
|
CommentAdded commentAdded,
|
|
CommentsUtil commentsUtil,
|
|
EmailReviewComments.Factory email,
|
|
PatchSetUtil psUtil,
|
|
PublishCommentUtil publishCommentUtil,
|
|
@Assisted PatchSet.Id psId,
|
|
@Assisted Project.NameKey projectNameKey) {
|
|
this.cmUtil = cmUtil;
|
|
this.changeNotesFactory = changeNotesFactory;
|
|
this.commentAdded = commentAdded;
|
|
this.commentsUtil = commentsUtil;
|
|
this.email = email;
|
|
this.psId = psId;
|
|
this.publishCommentUtil = publishCommentUtil;
|
|
this.psUtil = psUtil;
|
|
this.projectNameKey = projectNameKey;
|
|
}
|
|
|
|
@Override
|
|
public boolean updateChange(ChangeContext ctx)
|
|
throws ResourceConflictException, UnprocessableEntityException, IOException,
|
|
PatchListNotAvailableException, CommentsRejectedException {
|
|
user = ctx.getIdentifiedUser();
|
|
comments = commentsUtil.draftByChangeAuthor(ctx.getNotes(), ctx.getUser().getAccountId());
|
|
|
|
// PublishCommentsOp should update a separate ChangeUpdate Object than the one used by other ops
|
|
// For example, with the "publish comments on PS upload" workflow,
|
|
// There are 2 ops: ReplaceOp & PublishCommentsOp, where each updates its own ChangeUpdate
|
|
// This is required since
|
|
// 1. a ChangeUpdate has only 1 change message
|
|
// 2. Each ChangeUpdate results in 1 commit in NoteDb
|
|
// We do it this way so that the execution results in 2 different commits in NoteDb
|
|
ChangeUpdate changeUpdate = ctx.getDistinctUpdate(psId);
|
|
publishCommentUtil.publish(ctx, changeUpdate, comments, null);
|
|
return insertMessage(ctx, changeUpdate);
|
|
}
|
|
|
|
@Override
|
|
public void postUpdate(Context ctx) {
|
|
if (message == null || comments.isEmpty()) {
|
|
return;
|
|
}
|
|
ChangeNotes changeNotes = changeNotesFactory.createChecked(projectNameKey, psId.changeId());
|
|
PatchSet ps = psUtil.get(changeNotes, psId);
|
|
NotifyResolver.Result notify = ctx.getNotify(changeNotes.getChangeId());
|
|
if (notify.shouldNotify()) {
|
|
RepoView repoView;
|
|
try {
|
|
repoView = ctx.getRepoView();
|
|
} catch (IOException ex) {
|
|
throw new StorageException(
|
|
String.format("Repository %s not found", ctx.getProject().get()), ex);
|
|
}
|
|
email
|
|
.create(notify, changeNotes, ps, user, message, comments, null, labelDelta, repoView)
|
|
.sendAsync();
|
|
}
|
|
commentAdded.fire(
|
|
changeNotes.getChange(),
|
|
ps,
|
|
ctx.getAccount(),
|
|
message.getMessage(),
|
|
ImmutableMap.of(),
|
|
ImmutableMap.of(),
|
|
ctx.getWhen());
|
|
}
|
|
|
|
private boolean insertMessage(ChangeContext ctx, ChangeUpdate changeUpdate) {
|
|
StringBuilder buf = new StringBuilder();
|
|
if (comments.size() == 1) {
|
|
buf.append("\n\n(1 comment)");
|
|
} else if (comments.size() > 1) {
|
|
buf.append(String.format("\n\n(%d comments)", comments.size()));
|
|
}
|
|
if (buf.length() == 0) {
|
|
return false;
|
|
}
|
|
message =
|
|
ChangeMessagesUtil.newMessage(
|
|
psId, user, ctx.getWhen(), "Patch Set " + psId.get() + ":" + buf, null);
|
|
cmUtil.addChangeMessage(changeUpdate, message);
|
|
return true;
|
|
}
|
|
}
|