/*******************************************************************************
*
* Copyright (c) 2004-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:
*
* Kohsuke Kawaguchi, Bruce Chapman, Daniel Dyer, Jean-Baptiste Quenot, Anton Kozak
*
*
*******************************************************************************/
package hudson.tasks;
import hudson.mail.BaseMailSender;
import hudson.tasks.mail.impl.BackToNormalBuildMail;
import hudson.tasks.mail.impl.FailureBuildMail;
import hudson.tasks.mail.impl.UnstableBuildMail;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.CheckPoint;
import hudson.model.Result;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
/**
* Core logic of sending out notification e-mail.
*
* @author Jesse Glick
* @author Kohsuke Kawaguchi
*/
public class MailSender extends BaseMailSender {
private List<AbstractProject> upstreamProjects = new ArrayList<AbstractProject>();
/**
* If true, only the first unstable build will be reported.
*/
private boolean dontNotifyEveryUnstableBuild;
/**
* If true, individuals will receive e-mails regarding who broke the build.
*/
private boolean sendToIndividuals;
public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals) {
this(recipients, dontNotifyEveryUnstableBuild, sendToIndividuals, DEFAULT_CHARSET);
}
public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals,
String charset) {
this(recipients, dontNotifyEveryUnstableBuild, sendToIndividuals, charset,
Collections.<AbstractProject>emptyList());
}
public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals,
String charset, Collection<AbstractProject> includeUpstreamCommitters) {
super(recipients, charset);
this.dontNotifyEveryUnstableBuild = dontNotifyEveryUnstableBuild;
this.sendToIndividuals = sendToIndividuals;
this.upstreamProjects.addAll(includeUpstreamCommitters);
}
public boolean execute(AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException {
try {
MimeMessage mail = getMail(build, listener);
if (mail != null) {
// if the previous e-mail was sent for a success, this new e-mail
// is not a follow up
AbstractBuild<?, ?> pb = build.getPreviousBuild();
if (pb != null && pb.getResult() == Result.SUCCESS) {
mail.removeHeader("In-Reply-To");
mail.removeHeader("References");
}
Address[] allRecipients = mail.getAllRecipients();
if (allRecipients != null) {
StringBuilder buf = new StringBuilder("Sending e-mails to:");
for (Address a : allRecipients) {
buf.append(' ').append(a);
}
listener.getLogger().println(buf);
Mailer.descriptor().send((HudsonMimeMessage) mail);
build.addAction(new MailMessageIdAction(mail.getMessageID()));
} else {
listener.getLogger().println(Messages.MailSender_ListEmpty());
}
}
} catch (MessagingException e) {
e.printStackTrace(listener.error(e.getMessage()));
} finally {
CHECKPOINT.report();
}
return true;
}
/**
* To correctly compute the state change from the previous build to this
* build, we need to ignore aborted builds. See
* http://www.nabble.com/Losing-build-state-after-aborts--td24335949.html
*
* <p> And since we are consulting the earlier result, we need to wait for
* the previous build to pass the check point.
*/
private Result findPreviousBuildResult(AbstractBuild<?, ?> b) throws InterruptedException {
CHECKPOINT.block();
do {
b = b.getPreviousBuild();
if (b == null) {
return null;
}
} while (b.getResult() == Result.ABORTED);
return b.getResult();
}
protected MimeMessage getMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException, InterruptedException {
if (build.getResult() == Result.FAILURE) {
return new FailureBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset()).
getMail(build, listener);
}
if (build.getResult() == Result.UNSTABLE) {
if (!dontNotifyEveryUnstableBuild) {
return new UnstableBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset()).
getMail(build, listener);
}
Result prev = findPreviousBuildResult(build);
if (prev == Result.SUCCESS) {
return new UnstableBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset()).
getMail(build, listener);
}
}
if (build.getResult() == Result.SUCCESS) {
Result prev = findPreviousBuildResult(build);
if (prev == Result.FAILURE) {
return new BackToNormalBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset(),
Messages.MailSender_BackToNormal_Normal()).getMail(build, listener);
}
if (prev == Result.UNSTABLE) {
return new BackToNormalBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset(),
Messages.MailSender_BackToNormal_Stable()).getMail(build, listener);
}
}
return null;
}
/**
* Check whether a path (/-separated) will be archived.
*/
//TODO investigate where it's used.
protected boolean artifactMatches(String path, AbstractBuild<?, ?> build) {
return false;
}
/**
* Sometimes the outcome of the previous build affects the e-mail we send,
* hence this checkpoint.
*/
private static final CheckPoint CHECKPOINT = new CheckPoint("mail sent");
}