Line data Source code
1 : // Copyright (C) 2020 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.util; 16 : 17 : import com.google.common.flogger.FluentLogger; 18 : import com.google.gerrit.entities.Account; 19 : import com.google.gerrit.entities.Change; 20 : import com.google.gerrit.server.CurrentUser; 21 : import com.google.gerrit.server.IdentifiedUser; 22 : import com.google.gerrit.server.change.NotifyResolver; 23 : import com.google.gerrit.server.config.SendEmailExecutor; 24 : import com.google.gerrit.server.mail.send.AddToAttentionSetSender; 25 : import com.google.gerrit.server.mail.send.AttentionSetSender; 26 : import com.google.gerrit.server.mail.send.MessageIdGenerator; 27 : import com.google.gerrit.server.mail.send.MessageIdGenerator.MessageId; 28 : import com.google.gerrit.server.mail.send.RemoveFromAttentionSetSender; 29 : import com.google.gerrit.server.update.Context; 30 : import com.google.inject.Inject; 31 : import com.google.inject.assistedinject.Assisted; 32 : import java.io.IOException; 33 : import java.io.UncheckedIOException; 34 : import java.util.Optional; 35 : import java.util.concurrent.ExecutorService; 36 : import java.util.concurrent.Future; 37 : 38 : public class AttentionSetEmail { 39 13 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 40 : 41 : public interface Factory { 42 : 43 : /** 44 : * factory for sending an email when adding users to the attention set or removing them from it. 45 : * 46 : * @param sender sender in charge of sending the email, can be {@link AddToAttentionSetSender} 47 : * or {@link RemoveFromAttentionSetSender}. 48 : * @param ctx context for sending the email. 49 : * @param change the change that the user was added/removed in. 50 : * @param reason reason for adding/removing the user. 51 : * @param attentionUserId the user added/removed. 52 : */ 53 : AttentionSetEmail create( 54 : AttentionSetSender sender, 55 : Context ctx, 56 : Change change, 57 : String reason, 58 : Account.Id attentionUserId); 59 : } 60 : 61 : private final ExecutorService sendEmailsExecutor; 62 : private final AsyncSender asyncSender; 63 : 64 : @Inject 65 : AttentionSetEmail( 66 : @SendEmailExecutor ExecutorService executor, 67 : ThreadLocalRequestContext requestContext, 68 : MessageIdGenerator messageIdGenerator, 69 : AccountTemplateUtil accountTemplateUtil, 70 : @Assisted AttentionSetSender sender, 71 : @Assisted Context ctx, 72 : @Assisted Change change, 73 : @Assisted String reason, 74 13 : @Assisted Account.Id attentionUserId) { 75 13 : this.sendEmailsExecutor = executor; 76 : 77 : MessageId messageId; 78 : try { 79 13 : messageId = 80 13 : messageIdGenerator.fromChangeUpdateAndReason( 81 13 : ctx.getRepoView(), change.currentPatchSetId(), "AttentionSetEmail"); 82 0 : } catch (IOException e) { 83 0 : throw new UncheckedIOException(e); 84 13 : } 85 : 86 13 : this.asyncSender = 87 : new AsyncSender( 88 : requestContext, 89 13 : ctx.getIdentifiedUser(), 90 : sender, 91 : messageId, 92 13 : ctx.getNotify(change.getId()), 93 : attentionUserId, 94 13 : accountTemplateUtil.replaceTemplates(reason), 95 13 : change.getId()); 96 13 : } 97 : 98 : public void sendAsync() { 99 : @SuppressWarnings("unused") 100 13 : Future<?> possiblyIgnoredError = sendEmailsExecutor.submit(asyncSender); 101 13 : } 102 : 103 : /** 104 : * {@link Runnable} that sends the email asynchonously. 105 : * 106 : * <p>Only pass objects into this class that are thread-safe (e.g. immutable) so that they can be 107 : * safely accessed from the background thread. 108 : */ 109 : private static class AsyncSender implements Runnable, RequestContext { 110 : private final ThreadLocalRequestContext requestContext; 111 : private final IdentifiedUser user; 112 : private final AttentionSetSender sender; 113 : private final MessageIdGenerator.MessageId messageId; 114 : private final NotifyResolver.Result notify; 115 : private final Account.Id attentionUserId; 116 : private final String reason; 117 : private final Change.Id changeId; 118 : 119 : AsyncSender( 120 : ThreadLocalRequestContext requestContext, 121 : IdentifiedUser user, 122 : AttentionSetSender sender, 123 : MessageIdGenerator.MessageId messageId, 124 : NotifyResolver.Result notify, 125 : Account.Id attentionUserId, 126 : String reason, 127 13 : Change.Id changeId) { 128 13 : this.requestContext = requestContext; 129 13 : this.user = user; 130 13 : this.sender = sender; 131 13 : this.messageId = messageId; 132 13 : this.notify = notify; 133 13 : this.attentionUserId = attentionUserId; 134 13 : this.reason = reason; 135 13 : this.changeId = changeId; 136 13 : } 137 : 138 : @Override 139 : public void run() { 140 13 : RequestContext old = requestContext.setContext(this); 141 : try { 142 : Optional<Account.Id> accountId = 143 13 : user.isIdentifiedUser() 144 13 : ? Optional.of(user.asIdentifiedUser().getAccountId()) 145 13 : : Optional.empty(); 146 13 : if (accountId.isPresent()) { 147 13 : sender.setFrom(accountId.get()); 148 : } 149 13 : sender.setNotify(notify); 150 13 : sender.setAttentionSetUser(attentionUserId); 151 13 : sender.setReason(reason); 152 13 : sender.setMessageId(messageId); 153 13 : sender.send(); 154 0 : } catch (Exception e) { 155 0 : logger.atSevere().withCause(e).log("Cannot email update for change %s", changeId); 156 : } finally { 157 13 : requestContext.setContext(old); 158 : } 159 13 : } 160 : 161 : @Override 162 : public String toString() { 163 0 : return "send-email attention-set-update"; 164 : } 165 : 166 : @Override 167 : public CurrentUser getUser() { 168 13 : return user; 169 : } 170 : } 171 : }