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("&lt;html&gt;&lt;body&gt;");
+ * msg.append("&lt;img src=cid:").append(he.embed(img)).append("&gt;");
+ * msg.append("&lt;img src=cid:").append(he.embed(png)).append("&gt;");
+ * msg.append("&lt;/body&gt;&lt;/html&gt;");
+ * 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("&lt;html&gt;&lt;img src=cid:" +
-     *  embed(new URL("file:/my/image.gif"),"image.gif") +
-     *  "&gt;&lt;/html&gt;");
-     * // 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]

Reply via email to