/******************************************************************************* * * 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(); } }