Skip to content
Permalink
Browse files

Implementing attachments (JENKINS-9018)

  • Loading branch information
slide committed Jul 24, 2011
1 parent d13efd0 commit a27a81bc079ae69cf01baeb609b4408c77e6eeb3
@@ -2,7 +2,10 @@

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
@@ -13,6 +16,7 @@
import hudson.plugins.emailext.plugins.ContentBuilder;
import hudson.plugins.emailext.plugins.EmailTrigger;
import hudson.plugins.emailext.plugins.EmailTriggerDescriptor;
import hudson.remoting.VirtualChannel;
import hudson.scm.ChangeLogSet.Entry;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
@@ -21,15 +25,26 @@
import hudson.tasks.Notifier;
import hudson.tasks.Publisher;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.activation.MimetypesFileTypeMap;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.internet.MimeMultipart;

import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -123,6 +138,11 @@ public static EmailTriggerDescriptor getEmailTriggerType(String mailerId) {
* The default body of the emails for this project. ($PROJECT_DEFAULT_BODY)
*/
public String defaultContent;

/**
* The project wide set of attachments.
*/
public String attachmentsPattern;

/**
* Get the list of configured email triggers for this project.
@@ -277,7 +297,16 @@ private MimeMessage createMail(EmailType type, AbstractBuild<?, ?> build, BuildL

setSubject(type, build, msg, charset);

setContent(type, build, msg, charset);
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(getContent(type, build, msg, charset));
List<MimeBodyPart> attachments = getAttachments(type, build, msg, charset, listener);
if(attachments != null) {
for(MimeBodyPart attachment : attachments) {
multipart.addBodyPart(attachment);
}
}

msg.setContent(multipart);

EnvVars env = build.getEnvironment(listener);

@@ -376,7 +405,7 @@ private String getRecipientList(final EmailType type, final AbstractBuild<?, ?>
return recipients;
}

private void setContent(final EmailType type, final AbstractBuild<?, ?> build, MimeMessage msg, String charset)
private MimeBodyPart getContent(final EmailType type, final AbstractBuild<?, ?> build, MimeMessage msg, String charset)
throws MessagingException {
final String text = new ContentBuilder().transformText(type.getBody(), this, type, build);

@@ -392,7 +421,70 @@ private void setContent(final EmailType type, final AbstractBuild<?, ?> build, M
}
messageContentType += "; charset=" + charset;

msg.setContent(text, messageContentType);
// set the email message text
// (plain text or HTML depending on the content type)
MimeBodyPart msgPart = new MimeBodyPart();
msgPart.setContent(text, messageContentType);
return msgPart;
}


@SuppressWarnings("serial")
private List<MimeBodyPart> getAttachments(final EmailType type, final AbstractBuild<?, ?> build, MimeMessage msg, String charset, final BuildListener listener)
throws MessagingException, InterruptedException, IOException {
List<MimeBodyPart> attachments = null;
FilePath ws = build.getWorkspace();
if(ws == null) {
listener.getLogger().println("Error: No workspace found!");
} else if(attachmentsPattern != null && attachmentsPattern.trim().length() > 0) {
attachments = ws.act(new FileCallable<List<MimeBodyPart>>() {
public List<MimeBodyPart> invoke(File baseDir, VirtualChannel channel)
throws IOException {
long totalAttachmentSize = 0;
final long maxAttachmentSize =
ExtendedEmailPublisher.DESCRIPTOR.getMaxAttachmentSize();

final MimetypesFileTypeMap mimeTypeMap = new MimetypesFileTypeMap();
List<MimeBodyPart> results = new ArrayList<MimeBodyPart>();
FileSet src = Util.createFileSet(baseDir,attachmentsPattern);
DirectoryScanner ds = src.getDirectoryScanner();
for( String f : ds.getIncludedFiles() ) {
final File file = new File(baseDir, f);
if(!file.isFile()) {
listener.getLogger().println("Skipping `" + file.getName() + "' - not a file");
continue;
} else if(maxAttachmentSize > 0 &&
(totalAttachmentSize + file.length()) >= maxAttachmentSize) {
listener.getLogger().println("Skipping `" + file.getName() + "' ("+ file.length() + " bytes) - too large for maximum attachments size");
continue;
}

MimeBodyPart attachmentPart = new MimeBodyPart();
FileDataSource fileDataSource = new FileDataSource(file.getPath()) {
@Override
public String getContentType() {
return mimeTypeMap.getContentType(file.getName());
}
};

try {
attachmentPart.setDataHandler(new DataHandler(fileDataSource));
attachmentPart.setFileName(file.getName());
results.add(attachmentPart);

totalAttachmentSize += file.length();
} catch(MessagingException e) {
listener.getLogger().println("Error adding `" +
file.getName() + "' as attachment - " +
e.getMessage());
}
}
return results;
}
});
}

return attachments;
}

private static void addAddressesFromRecipientList(Set<InternetAddress> addresses, String recipientList,
@@ -88,6 +88,11 @@
* This is a global default recipient list for sending emails.
*/
private String recipientList = "";

/**
* The maximum size of all the attachments (in MB)
*/
private long maxAttachmentSize = -1;

private boolean overrideGlobalSettings;

@@ -206,6 +211,14 @@ public String getDefaultBody() {
public String getDefaultRecipients() {
return recipientList;
}

public long getMaxAttachmentSize() {
return maxAttachmentSize;
}

public long getMaxAttachmentSizeMb() {
return maxAttachmentSize / (1024 * 1024);
}

public boolean getOverrideGlobalSettings() {
return overrideGlobalSettings;
@@ -233,6 +246,7 @@ public Publisher newInstance(StaplerRequest req, JSONObject formData)
m.contentType = formData.getString("project_content_type");
m.defaultSubject = formData.getString("project_default_subject");
m.defaultContent = formData.getString("project_default_content");
m.attachmentsPattern = formData.getString("project_attachments");
m.configuredTriggers = new ArrayList<EmailTrigger>();

// Create a new email trigger for each one that is configured
@@ -314,7 +328,11 @@ public boolean configure(StaplerRequest req, JSONObject formData)
defaultBody = nullify(req.getParameter("ext_mailer_default_body"));
recipientList = nullify(req.getParameter("ext_mailer_default_recipients")) != null ?
req.getParameter("ext_mailer_default_recipients") : "";


// convert the value into megabytes (1024 * 1024 bytes)
maxAttachmentSize = nullify(req.getParameter("ext_mailer_max_attachment_size")) != null ?
(Long.parseLong(req.getParameter("ext_mailer_max_attachment_size")) * 1024 * 1024) : -1;

overrideGlobalSettings = req.getParameter("ext_mailer_override_global_settings") != null;

precedenceBulk = req.getParameter("extmailer.addPrecedenceBulk") != null;
@@ -356,4 +374,19 @@ public FormValidation doRecipientListRecipientsCheck(@QueryParameter final Strin
throws IOException, ServletException {
return new EmailRecepientUtils().validateFormRecipientList(value);
}

public FormValidation doMaxAttachmentSizeCheck(@QueryParameter final String value)
throws IOException, ServletException {
try {
String testValue = value.trim();
// we support an empty value (which means default)
// or a number
if(testValue.length() > 0) {
Long.parseLong(testValue);
}
return FormValidation.ok();
} catch (Exception e) {
return FormValidation.error(e.getMessage());
}
}
}
@@ -83,6 +83,22 @@
</j:choose>
</f:entry>

<!-- This is the default attachments set for the projcet. -->
<f:entry title="${%Attachments}"
help="/plugin/email-ext/help/projectConfig/attachments.html"
description="${%description('http://ant.apache.org/manual/Types/fileset.html')}">
<j:choose>
<j:when test="${instance.configured}">
<input class="setting-input" name="project_attachments"
type="text" value="${instance.attachmentsPattern}"/>
</j:when>
<j:otherwise>
<input class="setting-input" name="project_attachments"
type="text" value=""/>
</j:otherwise>
</j:choose>
</f:entry>

<!-- This is the help section. It displays a bunch of dynamic help for all content tokens. -->
<tr>
<td></td>
@@ -1,2 +1,6 @@
projectContentType.plainText=Plain Text (text/plain)
projectContentType.html=HTML (text/html)
description=Can use wildcards like ''module/dist/**/*.zip''. \
See the <a href="{0}">@includes of Ant fileset</a> for the exact format. \
The base directory is <a href="ws/">the workspace</a>.

@@ -80,6 +80,21 @@
<input class="setting-input" name="ext_mailer_default_subject"
type="text" value="${descriptor.defaultSubject}"/>
</f:entry>
<f:entry title="${%Maximum Attachment Size}"
help="/plugin/email-ext/help/globalConfig/maxAttachmentSize.html">
<j:choose>
<j:when test="${descriptor.maxAttachmentSize>0}">
<input class="setting-input" name="ext_mailer_max_attachment_size"
type="text" value="${descriptor.maxAttachmentSizeMb}"
checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/maxAttachmentSizeCheck?value='+encodeURIComponent(this.value)"/>
</j:when>
<j:otherwise>
<input class="setting-input" name="ext_mailer_max_attachment_size"
type="text" value=""
checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/maxAttachmentSizeCheck?value='+encodeURIComponent(this.value)"/>
</j:otherwise>
</j:choose>
</f:entry>
<f:entry title="${%Default Content}"
help="/plugin/email-ext/help/globalConfig/defaultBody.html">
<f:textarea class="setting-input"
@@ -0,0 +1,4 @@
<div>
The maximum size (in MB) for all attachments. If left blank,
there is no limit to the size of all attachments.
</div>
@@ -0,0 +1,4 @@
<div>
This is the set of attachments that will be used for the email. The format is a
comma separated list of Ant include file syntax.
</div>
@@ -16,6 +16,11 @@

import java.util.List;

import javax.mail.Message;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.matchers.JUnitMatchers.*;
@@ -35,6 +40,7 @@ public void setUp()
publisher = new ExtendedEmailPublisher();
publisher.defaultSubject = "%DEFAULT_SUBJECT";
publisher.defaultContent = "%DEFAULT_CONTENT";
publisher.attachmentsPattern = "";

project = createFreeStyleProject();
project.getPublishersList().add( publisher );
@@ -236,8 +242,25 @@ public void testShouldSendEmailUsingUtf8ByDefault()

Mailbox mailbox = Mailbox.get( "ashlux@gmail.com" );
assertEquals( "We should an email since the build failed.", 1, mailbox.size() );
assertThat( "UTF-8 charset should be used.", mailbox.get( 0 ).getContentType(),
containsString( "charset=UTF-8" ) );
Message msg = mailbox.get(0);
assertThat( "Message should be multipart", msg.getContentType(),
containsString("multipart/mixed"));

// TODO: add more tests for getting the multipart information.
if(MimeMessage.class.isInstance(msg)) {
MimeMessage mimeMsg = (MimeMessage)msg;
assertEquals( "Message content should be a MimeMultipart instance",
MimeMultipart.class, mimeMsg.getContent().getClass());
MimeMultipart multipart = (MimeMultipart)mimeMsg.getContent();
assertTrue( "There should be at least one part in the email",
multipart.getCount() >= 1);
MimeBodyPart bodyPart = (MimeBodyPart) multipart.getBodyPart(0);
assertThat( "UTF-8 charset should be used.", bodyPart.getContentType(),
containsString( "charset=UTF-8" ) );
} else {
assertThat( "UTF-8 charset should be used.", mailbox.get( 0 ).getContentType(),
containsString( "charset=UTF-8" ) );
}
}

public void testNewInstance_shouldGetBasicInformation()
@@ -248,13 +271,15 @@ public void testNewInstance_shouldGetBasicInformation()
form.put( "recipientlist_recipients", "ashlux@gmail.com" );
form.put( "project_default_subject", "Make millions in Nigeria" );
form.put( "project_default_content", "Give me a $1000 check and I'll mail you back $5000!!!" );
form.put( "project_attachments", "");

publisher = (ExtendedEmailPublisher) ExtendedEmailPublisher.DESCRIPTOR.newInstance( null, form );

assertEquals( "default", publisher.contentType );
assertEquals( "ashlux@gmail.com", publisher.recipientList );
assertEquals( "Make millions in Nigeria", publisher.defaultSubject );
assertEquals( "Give me a $1000 check and I'll mail you back $5000!!!", publisher.defaultContent );
assertEquals( "", publisher.attachmentsPattern);
}

private void addEmailType( EmailTrigger trigger )

0 comments on commit a27a81b

Please sign in to comment.
You can’t perform that action at this time.