/*******************************************************************************
*
* Copyright (c) 2011 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Anton Kozak
*
*******************************************************************************/
package hudson.tasks.mail.impl;
import hudson.Util;
import hudson.tasks.mail.BuildResultMail;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Hudson;
import hudson.model.User;
import hudson.tasks.HudsonMimeMessage;
import hudson.tasks.MailMessageIdAction;
import hudson.tasks.MailSender;
import hudson.tasks.Mailer;
import hudson.tasks.Messages;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.collections.CollectionUtils;
/**
* Base class for all project build result mails.
*/
public abstract class BaseBuildResultMail implements BuildResultMail {
protected static final int MAX_LOG_LINES = Integer.getInteger(MailSender.class.getName() + ".maxLogLines", 250);
//TODO where it's used?
public static boolean debug = false;
/**
* Whitespace-separated list of e-mail addresses that represent recipients.
*/
private String recipients;
/**
* The charset to use for the text and subject.
*/
private String charset;
/**
* The list of upstream projects.
*/
private List<AbstractProject> upstreamProjects;
/**
* If true, individuals will receive e-mails regarding who broke the build.
*/
private boolean sendToIndividuals;
public BaseBuildResultMail(String recipients, boolean sendToIndividuals, List<AbstractProject> upstreamProjects,
String charset) {
this.recipients = recipients;
this.sendToIndividuals = sendToIndividuals;
this.upstreamProjects = upstreamProjects;
this.charset = charset;
}
/**
* Returns recipients.
*
* @return recipients.
*/
public String getRecipients() {
return recipients;
}
/**
* Returns charset.
*
* @return charset.
*/
public String getCharset() {
return charset;
}
/**
* Returns prefix for subject of automatically generated emails.
*
* @return prefix for subject.
*/
protected String getSubjectPrefix() {
return hudson.mail.Messages.hudson_email_subject_prefix();
}
/**
* Creates empty mail.
*
* @param build build.
* @param listener listener.
* @return empty mail.
* @throws MessagingException exception if any.
*/
protected MimeMessage createEmptyMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException {
MimeMessage msg = new HudsonMimeMessage(Mailer.descriptor().createSession());
// TODO: I'd like to put the URL to the page in here,
// but how do I obtain that?
msg.setContent("", "text/plain");
msg.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress()));
msg.setSentDate(new Date());
Set<InternetAddress> rcp = new LinkedHashSet<InternetAddress>();
StringTokenizer tokens = new StringTokenizer(getRecipients());
while (tokens.hasMoreTokens()) {
String address = tokens.nextToken();
if (address.startsWith("upstream-individuals:")) {
// people who made a change in the upstream
String projectName = address.substring("upstream-individuals:".length());
AbstractProject up = Hudson.getInstance().getItemByFullName(projectName, AbstractProject.class);
if (up == null) {
listener.getLogger().println("No such project exist: " + projectName);
continue;
}
includeCulpritsOf(up, build, listener, rcp);
} else {
// ordinary address
try {
rcp.add(new InternetAddress(address));
} catch (AddressException e) {
// report bad address, but try to send to other addresses
e.printStackTrace(listener.error(e.getMessage()));
}
}
}
if (CollectionUtils.isNotEmpty(upstreamProjects)) {
for (AbstractProject project : upstreamProjects) {
includeCulpritsOf(project, build, listener, rcp);
}
}
if (sendToIndividuals) {
Set<User> culprits = build.getCulprits();
if (debug) {
listener.getLogger().println("Trying to send e-mails to individuals who broke the build. sizeof(culprits)==" + culprits.size());
}
rcp.addAll(buildCulpritList(listener, culprits));
}
msg.setRecipients(Message.RecipientType.TO, rcp.toArray(new InternetAddress[rcp.size()]));
AbstractBuild<?, ?> pb = build.getPreviousBuild();
if (pb != null) {
MailMessageIdAction b = pb.getAction(MailMessageIdAction.class);
if (b != null) {
msg.setHeader("In-Reply-To", b.messageId);
msg.setHeader("References", b.messageId);
}
}
return msg;
}
/**
* Appends build URL to the builder.
*
* @param build build.
* @param buf {@link StringBuilder}.
*/
protected void appendBuildUrl(AbstractBuild<?, ?> build, StringBuilder buf) {
appendUrl(Util.encode(build.getUrl())
+ (build.getChangeSet().isEmptySet() ? "" : "changes"), buf);
}
/**
* Appends URL to the builder.
*
* @param url url.
* @param buf {@link StringBuilder}.
*/
protected void appendUrl(String url, StringBuilder buf) {
String baseUrl = Mailer.descriptor().getUrl();
if (baseUrl != null) {
buf.append(Messages.MailSender_Link(baseUrl, url)).append("\n\n");
}
}
/**
* Appends footer to the mail builder.
*
* @param buf {@link StringBuilder}.
*/
protected void appendFooter(StringBuilder buf) {
String footer = getTextFooter();
if (footer != null) {
buf.append(footer);
}
}
/**
* Returns the subject of the mail.
*
* @param build build.
* @param caption the caption.
* @return prepared subject.
*/
protected String getSubject(AbstractBuild<?, ?> build, String caption) {
return new StringBuilder().append(getSubjectPrefix())
.append(" ")
.append(caption)
.append(" ")
.append(build.getFullDisplayName())
.toString();
}
private void includeCulpritsOf(AbstractProject upstreamProject, AbstractBuild<?, ?> currentBuild, BuildListener listener, Set<InternetAddress> recipientList) throws AddressException {
AbstractBuild<?, ?> upstreamBuild = currentBuild.getUpstreamRelationshipBuild(upstreamProject);
AbstractBuild<?, ?> previousBuild = currentBuild.getPreviousBuild();
AbstractBuild<?, ?> previousBuildUpstreamBuild = previousBuild != null ? previousBuild.getUpstreamRelationshipBuild(upstreamProject) : null;
if (previousBuild == null && upstreamBuild == null && previousBuildUpstreamBuild == null) {
listener.getLogger().println("Unable to compute the changesets in " + upstreamProject + ". Is the fingerprint configured?");
return;
}
if (previousBuild == null || upstreamBuild == null || previousBuildUpstreamBuild == null) {
listener.getLogger().println("Unable to compute the changesets in " + upstreamProject);
return;
}
AbstractBuild<?, ?> b = previousBuildUpstreamBuild;
do {
recipientList.addAll(buildCulpritList(listener, b.getCulprits()));
b = b.getNextBuild();
} while (b != upstreamBuild && b != null);
}
private Set<InternetAddress> buildCulpritList(BuildListener listener, Set<User> culprits) throws AddressException {
Set<InternetAddress> r = new HashSet<InternetAddress>();
for (User a : culprits) {
String adrs = Util.fixEmpty(a.getProperty(Mailer.UserProperty.class).getAddress());
if (debug) {
listener.getLogger().println(" User " + a.getId() + " -> " + adrs);
}
if (adrs != null) {
r.add(new InternetAddress(adrs));
} else {
listener.getLogger().println(Messages.MailSender_NoAddress(a.getFullName()));
}
}
return r;
}
/**
* Returns text footer for all automatically generated emails.
*
* @return text footer.
*/
private String getTextFooter() {
return hudson.mail.Messages.hudson_email_footer();
}
}