Author: bspeakmon
Date: Sat Jun 9 16:46:00 2007
New Revision: 545815
URL: http://svn.apache.org/viewvc?view=rev&rev=545815
Log:
- EMAIL-35: Embedding raw DataSource Attachments
- added embed methods for File objects
- Javadoc fixes
- delete temp files created during test runs
Modified:
jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java
jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java
Modified:
jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java?view=diff&rev=545815&r1=545814&r2=545815
==============================================================================
---
jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java
(original)
+++
jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java
Sat Jun 9 16:46:00 2007
@@ -16,6 +16,7 @@
*/
package org.apache.commons.mail;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -25,6 +26,8 @@
import java.util.Map;
import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.activation.FileDataSource;
import javax.activation.URLDataSource;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
@@ -38,17 +41,41 @@
* can also be set for HTML unaware email clients, such as text-based
* email clients.
*
- * <p>This class also inherits from MultiPartEmail, so it is easy to
+ * <p>This class also inherits from [EMAIL PROTECTED] MultiPartEmail}, so it
is easy to
* add attachments to the email.
*
- * <p>To send an email in HTML, one should create a HtmlEmail, then
- * use the setFrom, addTo, etc. methods. The HTML content can be set
- * with the setHtmlMsg method. The alternative text content can be set
- * with setTextMsg.
+ * <p>To send an email in HTML, one should create a <code>HtmlEmail</code>,
then
+ * use the [EMAIL PROTECTED] #setFrom(String)}, [EMAIL PROTECTED]
#addTo(String)} etc. methods.
+ * The HTML content can be set with the [EMAIL PROTECTED] #setHtmlMsg(String)}
method. The
+ * alternative text content can be set with [EMAIL PROTECTED]
#setTextMsg(String)}.
*
* <p>Either the text or HTML can be omitted, in which case the "main"
* part of the multipart becomes whichever is supplied rather than a
- * multipart/alternative.
+ * <code>multipart/alternative</code>.
+ *
+ * <h3>Embedding Images and Media</h3>
+ *
+ * <p>It is also possible to embed URLs, files, or arbitrary
+ * <code>DataSource</code>s directly into the body of the mail:
+ * <pre><code>
+ * HtmlEmail he = new HtmlEmail();
+ * File img = new File("my/image.gif");
+ * PNGDataSource png = new PNGDataSource(decodedPNGOutputStream); // a custom
class
+ * StringBuffer msg = new StringBuffer();
+ * msg.append("<html><body>");
+ * msg.append("<img src=cid:").append(he.embed(img)).append(">");
+ * msg.append("<img src=cid:").append(he.embed(png)).append(">");
+ * msg.append("</body></html>");
+ * he.setHtmlMsg(msg.toString());
+ * // code to set the other email fields (not shown)
+ * </pre></code>
+ *
+ * <p>Embedded entities are tracked by their name, which for
<code>File</code>s is
+ * the filename itself and for <code>URL</code>s is the canonical path. It is
+ * an error to bind the same name to more than one entity, and this class will
+ * attempt to validate that for <code>File</code>s and <code>URL</code>s. When
+ * embedding a <code>DataSource</code>, the code uses the <code>equals()</code>
+ * method defined on the <code>DataSource</code>s to make the determination.
*
* @since 1.0
* @author <a href="mailto:unknown">Regis Koenig</a>
@@ -76,8 +103,8 @@
protected String html;
/**
- * Embedded images Map<String,InlineImages> where the key is the
- * user-defined image name
+ * Embedded images Map<String, InlineImage> where the key is the
+ * user-defined image name.
*/
protected Map inlineImages = new HashMap();
@@ -97,7 +124,7 @@
throw new EmailException("Invalid message supplied");
}
- this.text = aText;
+ this.text = aText;
return this;
}
@@ -124,16 +151,16 @@
/**
* Set the message.
*
- * <p>This method overrides the MultiPartEmail setMsg() method in
- * order to send an HTML message instead of a full text message in
+ * <p>This method overrides [EMAIL PROTECTED]
MultiPartEmail#setMsg(String)} in
+ * order to send an HTML message instead of a plain text message in
* the mail body. The message is formatted in HTML for the HTML
- * part of the message, it is let as is in the alternate text
+ * part of the message; it is left as is in the alternate text
* part.
*
- * @param msg A String.
- * @return An Email.
- * @throws EmailException see javax.mail.internet.MimeBodyPart
- * for definitions
+ * @param msg the message text to use
+ * @return this <code>HtmlEmail</code>
+ * @throws EmailException if msg is null or empty;
+ * see javax.mail.internet.MimeBodyPart for definitions
* @since 1.0
*/
public Email setMsg(String msg) throws EmailException
@@ -161,22 +188,23 @@
}
/**
- * Embeds an URL in the HTML.
+ * Attempts to parse the specified <code>String</code> as a URL that will
+ * then be embedded in the message.
*
- * @param url The URL of the file.
+ * @param urlString String representation of the URL.
* @param name The name that will be set in the filename header field.
- * @return A String with the Content-ID of the file.
- * @throws EmailException when URL supplied is invalid
- * also see javax.mail.internet.MimeBodyPart for definitions
+ * @return A String with the Content-ID of the URL.
+ * @throws EmailException when URL supplied is invalid or if <code> is null
+ * or empty; also see [EMAIL PROTECTED] javax.mail.internet.MimeBodyPart}
for definitions
*
* @see #embed(URL, String)
* @since 1.1
*/
- public String embed(String url, String name) throws EmailException
+ public String embed(String urlString, String name) throws EmailException
{
try
{
- return embed(new URL(url), name);
+ return embed(new URL(urlString), name);
}
catch (MalformedURLException e)
{
@@ -187,20 +215,19 @@
/**
* Embeds an URL in the HTML.
*
- * <p>This method allows to embed a file located by an URL into
- * the mail body. It allows, for instance, to add inline images
+ * <p>This method embeds a file located by an URL into
+ * the mail body. It allows, for instance, to add inline images
* to the email. Inline files may be referenced with a
* <code>cid:xxxxxx</code> URL, where xxxxxx is the Content-ID
* returned by the embed function. It is an error to bind the same name
- * to more than one URL.
+ * to more than one URL; if the same URL is embedded multiple times, the
+ * same Content-ID is guaranteed to be returned.
*
- * <p>Example of use:<br><code><pre>
- * HtmlEmail he = new HtmlEmail();
- * he.setHtmlMsg("<html><img src=cid:" +
- * embed(new URL("file:/my/image.gif"),"image.gif") +
- * "></html>");
- * // code to set the others email fields (not shown)
- * </pre></code>
+ * <p>While functionally the same as passing <code>URLDataSource</code> to
+ * [EMAIL PROTECTED] #embed(DataSource, String, String)}, this method
attempts
+ * to validate the URL before embedding it in the message and will throw
+ * <code>EmailException</code> if the validation fails. In this case, the
+ * <code>HtmlEmail</code> object will not be changed.
*
* <p>
* NOTE: Clients should take care to ensure that different URLs are bound
to
@@ -213,29 +240,33 @@
* @param name The name that will be set in the filename header
* field.
* @return A String with the Content-ID of the file.
- * @throws EmailException when URL supplied is invalid
- * also see javax.mail.internet.MimeBodyPart for definitions
+ * @throws EmailException when URL supplied is invalid or if <code> is null
+ * or empty; also see [EMAIL PROTECTED] javax.mail.internet.MimeBodyPart}
for definitions
* @since 1.0
*/
public String embed(URL url, String name) throws EmailException
{
- InlineImage ii = null;
+ if (EmailUtils.isEmpty(name))
+ {
+ throw new EmailException("name cannot be null or empty");
+ }
- // check if the URL contents have already been attached;
+ // check if a URLDataSource for this name has already been attached;
// if so, return the cached CID value.
if (inlineImages.containsKey(name))
{
- ii = (InlineImage) inlineImages.get(name);
+ InlineImage ii = (InlineImage) inlineImages.get(name);
+ URLDataSource urlDataSource = (URLDataSource) ii.getDataSource();
// make sure the supplied URL points to the same thing
// as the one already associated with this name.
- if (url.equals(ii.getURL()))
+ if (url.equals(urlDataSource.getURL()))
{
return ii.getCid();
}
else
{
- throw new EmailException("embedded file name '" + name
- + " is already bound to URL " + ii.getURL().toString()
+ throw new EmailException("embedded name '" + name
+ + "' is already bound to URL " + urlDataSource.getURL()
+ "; existing names cannot be rebound");
}
// NOTE: Comparing URLs with URL.equals() is known to be
@@ -245,27 +276,207 @@
}
// verify that the URL is valid
+ InputStream is = null;
try
{
- InputStream is = url.openStream();
- is.close();
+ is = url.openStream();
}
catch (IOException e)
{
throw new EmailException("Invalid URL", e);
}
+ finally
+ {
+ try
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ }
+ catch (IOException ioe) { /* sigh */ }
+ }
+
+ return embed(new URLDataSource(url), name);
+ }
+
+ /**
+ * Embeds a file in the HTML. This implementation delegates to
+ * [EMAIL PROTECTED] #embed(File, String)}.
+ *
+ * @param file The <code>File</code> object to embed
+ * @return A String with the Content-ID of the file.
+ * @throws EmailException when the supplied <code>File</code> cannot be
+ * used; also see [EMAIL PROTECTED] javax.mail.internet.MimeBodyPart} for
definitions
+ *
+ * @see #embed(File, String)
+ * @since 1.1
+ */
+ public String embed(File file) throws EmailException
+ {
+ String cid =
EmailUtils.randomAlphabetic(HtmlEmail.CID_LENGTH).toLowerCase();
+ return embed(file, cid);
+ }
+
+ /**
+ * Embeds a file in the HTML.
+ *
+ * <p>This method embeds a file located by an URL into
+ * the mail body. It allows, for instance, to add inline images
+ * to the email. Inline files may be referenced with a
+ * <code>cid:xxxxxx</code> URL, where xxxxxx is the Content-ID
+ * returned by the embed function. Files are bound to their names, which is
+ * the value returned by [EMAIL PROTECTED] java.io.File#getName()}. If the
same file
+ * is embedded multiple times, the same CID is guaranteed to be returned.
+ *
+ * <p>While functionally the same as passing <code>FileDataSource</code> to
+ * [EMAIL PROTECTED] #embed(DataSource, String, String)}, this method
attempts
+ * to validate the file before embedding it in the message and will throw
+ * <code>EmailException</code> if the validation fails. In this case, the
+ * <code>HtmlEmail</code> object will not be changed.
+ *
+ * @param file The <code>File</code> to embed
+ * @param cid the Content-ID to use for the embedded <code>File</code>
+ * @return A String with the Content-ID of the file.
+ * @throws EmailException when the supplied <code>File</code> cannot be
used
+ * or if the file has already been embedded;
+ * also see [EMAIL PROTECTED] javax.mail.internet.MimeBodyPart} for
definitions
+ * @since 1.1
+ */
+ public String embed(File file, String cid) throws EmailException
+ {
+ if (EmailUtils.isEmpty(file.getName()))
+ {
+ throw new EmailException("file name cannot be null or empty");
+ }
+
+ // verify that the File can provide a canonical path
+ String filePath = null;
+ try
+ {
+ filePath = file.getCanonicalPath();
+ }
+ catch (IOException ioe)
+ {
+ throw new EmailException("couldn't get canonical path for "
+ + file.getName(), ioe);
+ }
+
+ // check if a FileDataSource for this name has already been attached;
+ // if so, return the cached CID value.
+ if (inlineImages.containsKey(file.getName()))
+ {
+ InlineImage ii = (InlineImage) inlineImages.get(file.getName());
+ FileDataSource fileDataSource = (FileDataSource)
ii.getDataSource();
+ // make sure the supplied file has the same canonical path
+ // as the one already associated with this name.
+ String existingFilePath = null;
+ try
+ {
+ existingFilePath = fileDataSource.getFile().getCanonicalPath();
+ }
+ catch (IOException ioe)
+ {
+ throw new EmailException("couldn't get canonical path for file
"
+ + fileDataSource.getFile().getName()
+ + "which has already been embedded", ioe);
+ }
+ if (filePath.equals(existingFilePath))
+ {
+ return ii.getCid();
+ }
+ else
+ {
+ throw new EmailException("embedded name '" + file.getName()
+ + "' is already bound to file " + existingFilePath
+ + "; existing names cannot be rebound");
+ }
+ }
+
+ // verify that the file is valid
+ if (!file.exists())
+ {
+ throw new EmailException("file " + filePath + " doesn't exist");
+ }
+ if (!file.isFile())
+ {
+ throw new EmailException("file " + filePath + " isn't a normal
file");
+ }
+ if (!file.canRead())
+ {
+ throw new EmailException("file " + filePath + " isn't readable");
+ }
+
+ return embed(new FileDataSource(file), file.getName());
+ }
+
+ /**
+ * Embeds the specified <code>DataSource</code> in the HTML using a
+ * randomly generated Content-ID. Returns the generated Content-ID string.
+ *
+ * @param dataSource the <code>DataSource</code> to embed
+ * @param name the name that will be set in the filename header field
+ * @return the generated Content-ID for this <code>DataSource</code>
+ * @throws EmailException if the embedding fails or if <code>name</code> is
+ * null or empty
+ * @see #embed(DataSource, String, String)
+ * @since 1.1
+ */
+ public String embed(DataSource dataSource, String name) throws
EmailException
+ {
+ // check if the DataSource has already been attached;
+ // if so, return the cached CID value.
+ if (inlineImages.containsKey(name))
+ {
+ InlineImage ii = (InlineImage) inlineImages.get(name);
+ // make sure the supplied URL points to the same thing
+ // as the one already associated with this name.
+ if (dataSource.equals(ii.getDataSource()))
+ {
+ return ii.getCid();
+ }
+ else
+ {
+ throw new EmailException("embedded DataSource '" + name
+ + "' is already bound to name " +
ii.getDataSource().toString()
+ + "; existing names cannot be rebound");
+ }
+ }
+
+ String cid =
EmailUtils.randomAlphabetic(HtmlEmail.CID_LENGTH).toLowerCase();
+ return embed(dataSource, name, cid);
+ }
+
+ /**
+ * Embeds the specified <code>DataSource</code> in the HTML using the
+ * specified Content-ID. Returns the specified Content-ID string.
+ *
+ * @param dataSource the <code>DataSource</code> to embed
+ * @param name the name that will be set in the filename header field
+ * @param cid the Content-ID to use for this <code>DataSource</code>
+ * @return the supplied Content-ID for this <code>DataSource</code>
+ * @throws EmailException if the embedding fails or if <code>name</code> is
+ * null or empty
+ * @since 1.1
+ */
+ public String embed(DataSource dataSource, String name, String cid)
+ throws EmailException
+ {
+ if (EmailUtils.isEmpty(name))
+ {
+ throw new EmailException("name cannot be null or empty");
+ }
MimeBodyPart mbp = new MimeBodyPart();
try
{
- mbp.setDataHandler(new DataHandler(new URLDataSource(url)));
+ mbp.setDataHandler(new DataHandler(dataSource));
mbp.setFileName(name);
mbp.setDisposition("inline");
- String cid =
EmailUtils.randomAlphabetic(HtmlEmail.CID_LENGTH).toLowerCase();
mbp.setContentID("<" + cid + ">");
- ii = new InlineImage(cid, url, mbp);
+ InlineImage ii = new InlineImage(cid, dataSource, mbp);
this.inlineImages.put(name, ii);
return cid;
@@ -382,8 +593,8 @@
{
/** content id */
private String cid;
- /** URL that points to the content */
- private URL url;
+ /** <code>DataSource</code> for the content */
+ private DataSource dataSource;
/** the <code>MimeBodyPart</code> that contains the encoded data */
private MimeBodyPart mbp;
@@ -391,14 +602,14 @@
* Creates an InlineImage object to represent the
* specified content ID and <code>MimeBodyPart</code>.
* @param cid the generated content ID
- * @param url the URL that points to the content
+ * @param dataSource the <code>DataSource</code> that represents the
content
* @param mbp the <code>MimeBodyPart</code> that contains the encoded
* data
*/
- public InlineImage(String cid, URL url, MimeBodyPart mbp)
+ public InlineImage(String cid, DataSource dataSource, MimeBodyPart mbp)
{
this.cid = cid;
- this.url = url;
+ this.dataSource = dataSource;
this.mbp = mbp;
}
@@ -412,12 +623,12 @@
}
/**
- * Returns the URL that points to the encoded content.
- * @return the URL pointing to the encoded content
+ * Returns the <code>DataSource</code> that represents the encoded
content.
+ * @return the <code>DataSource</code> representing the encoded content
*/
- public URL getURL()
+ public DataSource getDataSource()
{
- return url;
+ return dataSource;
}
/**
Modified:
jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java?view=diff&rev=545815&r1=545814&r2=545815
==============================================================================
---
jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java
(original)
+++
jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java
Sat Jun 9 16:46:00 2007
@@ -20,6 +20,8 @@
import java.io.IOException;
import java.net.URL;
+import javax.activation.FileDataSource;
+
import org.apache.commons.mail.mocks.MockHtmlEmailConcrete;
import org.apache.commons.mail.settings.EmailConfiguration;
@@ -154,7 +156,7 @@
*
* @throws Exception Exception
*/
- public void testEmbed() throws Exception
+ public void testEmbedUrl() throws Exception
{
// ====================================================================
// Test Success
@@ -205,6 +207,87 @@
}
}
+ public void testEmbedFile() throws Exception
+ {
+ // ====================================================================
+ // Test Success
+ // ====================================================================
+
+ File file = File.createTempFile("testEmbedFile", "txt");
+ file.deleteOnExit();
+ String strEmbed = this.email.embed(file);
+ assertNotNull(strEmbed);
+ assertEquals("generated CID has wrong length",
+ HtmlEmail.CID_LENGTH, strEmbed.length());
+
+ // if we embed the same file again, do we get the same content ID
+ // back?
+ String testCid =
+ this.email.embed(file);
+ assertEquals("didn't get same CID after embedding same file twice",
+ strEmbed, testCid);
+
+ // if we embed a new file, is the content ID unique?
+ File otherFile = File.createTempFile("testEmbedFile2", "txt");
+ otherFile.deleteOnExit();
+ String newCid = this.email.embed(otherFile);
+ assertFalse("didn't get unique CID from embedding new file",
+ strEmbed.equals(newCid));
+ }
+
+ public void testEmbedUrlAndFile() throws Exception
+ {
+ File tmpFile = File.createTempFile("testfile", "txt");
+ tmpFile.deleteOnExit();
+ String fileCid = this.email.embed(tmpFile);
+
+ URL fileUrl = tmpFile.toURL();
+ String urlCid = this.email.embed(fileUrl, "urlName");
+
+ assertFalse("file and URL cids should be different even for same
resource",
+ fileCid.equals(urlCid));
+ }
+
+ public void testEmbedDataSource() throws Exception
+ {
+ File tmpFile = File.createTempFile("testEmbedDataSource", "txt");
+ tmpFile.deleteOnExit();
+ FileDataSource dataSource = new FileDataSource(tmpFile);
+
+ // does embedding a datasource without a name fail?
+ try
+ {
+ this.email.embed(dataSource, "");
+ fail("embedding with an empty string for a name should fail");
+ }
+ catch (EmailException e)
+ {
+ // expected
+ }
+
+ // properly embed the datasource
+ String cid = this.email.embed(dataSource, "testname");
+
+ // does embedding the same datasource under the same name return
+ // the original cid?
+ String sameCid = this.email.embed(dataSource, "testname");
+ assertEquals("didn't get same CID for embedding same datasource twice",
+ cid, sameCid);
+
+ // does embedding another datasource under the same name fail?
+ File anotherFile = File.createTempFile("testEmbedDataSource2", "txt");
+ anotherFile.deleteOnExit();
+ FileDataSource anotherDS = new FileDataSource(anotherFile);
+ try
+ {
+ this.email.embed(anotherDS, "testname");
+ }
+ catch (EmailException e)
+ {
+ // expected
+ }
+ }
+
/**
* @throws EmailException when bad addresses and attachments are used
* @throws IOException if creating a temp file, URL or sending fails
@@ -216,6 +299,7 @@
/** File to used to test file attachments (Must be valid) */
testFile = File.createTempFile("commons-email-testfile", ".txt");
+ testFile.deleteOnExit();
// ====================================================================
// Test Success
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]