View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.mail;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.URL;
23  
24  import javax.activation.DataHandler;
25  import javax.activation.DataSource;
26  import javax.activation.FileDataSource;
27  import javax.activation.URLDataSource;
28  import javax.mail.BodyPart;
29  import javax.mail.MessagingException;
30  import javax.mail.internet.MimeBodyPart;
31  import javax.mail.internet.MimeMultipart;
32  import javax.mail.internet.MimePart;
33  
34  /**
35   * A multipart email.
36   *
37   * <p>This class is used to send multi-part internet email like
38   * messages with attachments.
39   *
40   * <p>To create a multi-part email, call the default constructor and
41   * then you can call setMsg() to set the message and call the
42   * different attach() methods.
43   *
44   * @since 1.0
45   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
46   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
47   * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
48   * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
49   * @author <a href="mailto:unknown">Regis Koenig</a>
50   * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
51   * @version $Id: MultiPartEmail.java 1420402 2012-12-11 20:59:15Z tn $
52   */
53  public class MultiPartEmail extends Email
54  {
55      /** Body portion of the email. */
56      private MimeMultipart container;
57  
58      /** The message container. */
59      private BodyPart primaryBodyPart;
60  
61      /** The MIME subtype. */
62      private String subType;
63  
64      /** Indicates if the message has been initialized. */
65      private boolean initialized;
66  
67      /** Indicates if attachments have been added to the message. */
68      private boolean boolHasAttachments;
69  
70      /**
71       * Set the MIME subtype of the email.
72       *
73       * @param aSubType MIME subtype of the email
74       * @since 1.0
75       */
76      public void setSubType(String aSubType)
77      {
78          this.subType = aSubType;
79      }
80  
81      /**
82       * Get the MIME subtype of the email.
83       *
84       * @return MIME subtype of the email
85       * @since 1.0
86       */
87      public String getSubType()
88      {
89          return subType;
90      }
91  
92      /**
93       * Add a new part to the email.
94       *
95       * @param partContent The content.
96       * @param partContentType The content type.
97       * @return An Email.
98       * @throws EmailException see javax.mail.internet.MimeBodyPart
99       *  for definitions
100      * @since 1.0
101      */
102     public Email addPart(String partContent, String partContentType)
103         throws EmailException
104     {
105             BodyPart bodyPart = createBodyPart();
106         try
107         {
108             bodyPart.setContent(partContent, partContentType);
109             getContainer().addBodyPart(bodyPart);
110         }
111         catch (MessagingException me)
112         {
113             throw new EmailException(me);
114         }
115 
116         return this;
117     }
118 
119     /**
120      * Add a new part to the email.
121      *
122      * @param multipart The MimeMultipart.
123      * @return An Email.
124      * @throws EmailException see javax.mail.internet.MimeBodyPart
125      *  for definitions
126      *  @since 1.0
127      */
128     public Email addPart(MimeMultipart multipart) throws EmailException
129     {
130         try
131         {
132             return addPart(multipart, getContainer().getCount());
133         }
134         catch (MessagingException me)
135         {
136             throw new EmailException(me);
137         }
138     }
139 
140     /**
141      * Add a new part to the email.
142      *
143      * @param multipart The part to add.
144      * @param index The index to add at.
145      * @return The email.
146      * @throws EmailException An error occured while adding the part.
147      * @since 1.0
148      */
149     public Email addPart(MimeMultipart multipart, int index) throws EmailException
150     {
151             BodyPart bodyPart = createBodyPart();
152         try
153         {
154             bodyPart.setContent(multipart);
155             getContainer().addBodyPart(bodyPart, index);
156         }
157         catch (MessagingException me)
158         {
159             throw new EmailException(me);
160         }
161 
162         return this;
163     }
164 
165     /**
166      * Initialize the multipart email.
167      * @since 1.0
168      */
169     protected void init()
170     {
171         if (initialized)
172         {
173             throw new IllegalStateException("Already initialized");
174         }
175 
176         container = createMimeMultipart();
177         super.setContent(container);
178 
179         initialized = true;
180     }
181 
182     /**
183      * Set the message of the email.
184      *
185      * @param msg A String.
186      * @return An Email.
187      * @throws EmailException see javax.mail.internet.MimeBodyPart
188      *  for definitions
189      * @since 1.0
190      */
191     @Override
192     public Email setMsg(String msg) throws EmailException
193     {
194         // throw exception on null message
195         if (EmailUtils.isEmpty(msg))
196         {
197             throw new EmailException("Invalid message supplied");
198         }
199         try
200         {
201             BodyPart primary = getPrimaryBodyPart();
202 
203             if ((primary instanceof MimePart) && EmailUtils.isNotEmpty(charset))
204             {
205                 ((MimePart) primary).setText(msg, charset);
206             }
207             else
208             {
209                 primary.setText(msg);
210             }
211         }
212         catch (MessagingException me)
213         {
214             throw new EmailException(me);
215         }
216         return this;
217     }
218 
219     /**
220      * Does the work of actually building the MimeMessage. Please note that
221      * a user rarely calls this method directly and only if he/she is
222      * interested in the sending the underlying MimeMessage without
223      * commons-email.
224      *
225      * @exception EmailException if there was an error.
226      * @since 1.0
227      */
228     @Override
229     public void buildMimeMessage() throws EmailException
230     {
231         try
232         {
233             if (primaryBodyPart != null)
234             {
235                 // before a multipart message can be sent, we must make sure that
236                 // the content for the main body part was actually set.  If not,
237                 // an IOException will be thrown during super.send().
238 
239                 BodyPart body = this.getPrimaryBodyPart();
240                 try
241                 {
242                     body.getContent();
243                 }
244                 catch (IOException e) // NOPMD
245                 {
246                     // do nothing here.
247                     // content will be set to an empty string as a result.
248                     // (Should this really be rethrown as an email exception?)
249                     // throw new EmailException(e);
250                 }
251             }
252 
253             if (subType != null)
254             {
255                 getContainer().setSubType(subType);
256             }
257 
258             super.buildMimeMessage();
259         }
260         catch (MessagingException me)
261         {
262             throw new EmailException(me);
263         }
264     }
265 
266     /**
267      * Attach a file.
268      *
269      * @param file A file attachment
270      * @return A MultiPartEmail.
271      * @throws EmailException see javax.mail.internet.MimeBodyPart
272      *  for definitions
273      * @since 1.3
274      */
275     public MultiPartEmail attach(File file)
276         throws EmailException
277     {
278         String fileName = file.getAbsolutePath();
279 
280         try
281         {
282             if (!file.exists())
283             {
284                 throw new IOException("\"" + fileName + "\" does not exist");
285             }
286 
287             FileDataSource fds = new FileDataSource(file);
288 
289             return attach(fds, file.getName(), null, EmailAttachment.ATTACHMENT);
290         }
291         catch (IOException e)
292         {
293             throw new EmailException("Cannot attach file \"" + fileName + "\"", e);
294         }
295     }
296 
297     /**
298      * Attach an EmailAttachment.
299      *
300      * @param attachment An EmailAttachment.
301      * @return A MultiPartEmail.
302      * @throws EmailException see javax.mail.internet.MimeBodyPart
303      *  for definitions
304      * @since 1.0
305      */
306     public MultiPartEmail attach(EmailAttachment attachment)
307         throws EmailException
308     {
309         MultiPartEmail result = null;
310 
311         if (attachment == null)
312         {
313             throw new EmailException("Invalid attachment supplied");
314         }
315 
316         URL url = attachment.getURL();
317 
318         if (url == null)
319         {
320             String fileName = null;
321             try
322             {
323                 fileName = attachment.getPath();
324                 File file = new File(fileName);
325                 if (!file.exists())
326                 {
327                     throw new IOException("\"" + fileName + "\" does not exist");
328                 }
329                 result =
330                     attach(
331                         new FileDataSource(file),
332                         attachment.getName(),
333                         attachment.getDescription(),
334                         attachment.getDisposition());
335             }
336             catch (IOException e)
337             {
338                 throw new EmailException("Cannot attach file \"" + fileName + "\"", e);
339             }
340         }
341         else
342         {
343             result =
344                 attach(
345                     url,
346                     attachment.getName(),
347                     attachment.getDescription(),
348                     attachment.getDisposition());
349         }
350 
351         return result;
352     }
353 
354     /**
355      * Attach a file located by its URL.  The disposition of the file
356      * is set to mixed.
357      *
358      * @param url The URL of the file (may be any valid URL).
359      * @param name The name field for the attachment.
360      * @param description A description for the attachment.
361      * @return A MultiPartEmail.
362      * @throws EmailException see javax.mail.internet.MimeBodyPart
363      *  for definitions
364      * @since 1.0
365      */
366     public MultiPartEmail attach(URL url, String name, String description)
367         throws EmailException
368     {
369         return attach(url, name, description, EmailAttachment.ATTACHMENT);
370     }
371 
372     /**
373      * Attach a file located by its URL.
374      *
375      * @param url The URL of the file (may be any valid URL).
376      * @param name The name field for the attachment.
377      * @param description A description for the attachment.
378      * @param disposition Either mixed or inline.
379      * @return A MultiPartEmail.
380      * @throws EmailException see javax.mail.internet.MimeBodyPart
381      *  for definitions
382      * @since 1.0
383      */
384     public MultiPartEmail attach(
385         URL url,
386         String name,
387         String description,
388         String disposition)
389         throws EmailException
390     {
391         // verify that the URL is valid
392        try
393        {
394            InputStream is = url.openStream();
395            is.close();
396        }
397        catch (IOException e)
398        {
399            throw new EmailException("Invalid URL set:" + url, e);
400        }
401 
402        return attach(new URLDataSource(url), name, description, disposition);
403     }
404 
405     /**
406      * Attach a file specified as a DataSource interface.
407      *
408      * @param ds A DataSource interface for the file.
409      * @param name The name field for the attachment.
410      * @param description A description for the attachment.
411      * @return A MultiPartEmail.
412      * @throws EmailException see javax.mail.internet.MimeBodyPart
413      *  for definitions
414      * @since 1.0
415      */
416     public MultiPartEmail attach(
417         DataSource ds,
418         String name,
419         String description)
420         throws EmailException
421     {
422         // verify that the DataSource is valid
423         try
424         {
425             final InputStream is = (ds != null) ? ds.getInputStream() : null;
426             if (is != null)
427             {
428                 // close the input stream to prevent file locking on windows
429                 is.close();
430             }
431 
432             if (is == null)
433             {
434                 throw new EmailException("Invalid Datasource");
435             }
436         }
437         catch (IOException e)
438         {
439             throw new EmailException("Invalid Datasource", e);
440         }
441 
442         return attach(ds, name, description, EmailAttachment.ATTACHMENT);
443     }
444 
445     /**
446      * Attach a file specified as a DataSource interface.
447      *
448      * @param ds A DataSource interface for the file.
449      * @param name The name field for the attachment.
450      * @param description A description for the attachment.
451      * @param disposition Either mixed or inline.
452      * @return A MultiPartEmail.
453      * @throws EmailException see javax.mail.internet.MimeBodyPart
454      *  for definitions
455      * @since 1.0
456      */
457     public MultiPartEmail attach(
458         DataSource ds,
459         String name,
460         String description,
461         String disposition)
462         throws EmailException
463     {
464         if (EmailUtils.isEmpty(name))
465         {
466             name = ds.getName();
467         }
468         BodyPart bodyPart = createBodyPart();
469         try
470         {
471             getContainer().addBodyPart(bodyPart);
472 
473             bodyPart.setDisposition(disposition);
474             bodyPart.setFileName(name);
475             bodyPart.setDescription(description);
476             bodyPart.setDataHandler(new DataHandler(ds));
477         }
478         catch (MessagingException me)
479         {
480             throw new EmailException(me);
481         }
482         setBoolHasAttachments(true);
483 
484         return this;
485     }
486 
487     /**
488      * Gets first body part of the message.
489      *
490      * @return The primary body part.
491      * @throws MessagingException An error occurred while getting the primary body part.
492      * @since 1.0
493      */
494     protected BodyPart getPrimaryBodyPart() throws MessagingException
495     {
496         if (!initialized)
497         {
498             init();
499         }
500 
501         // Add the first body part to the message.  The fist body part must be
502         if (this.primaryBodyPart == null)
503         {
504             primaryBodyPart = createBodyPart();
505             getContainer().addBodyPart(primaryBodyPart, 0);
506         }
507 
508         return primaryBodyPart;
509     }
510 
511     /**
512      * Gets the message container.
513      *
514      * @return The message container.
515      * @since 1.0
516      */
517     protected MimeMultipart getContainer()
518     {
519         if (!initialized)
520         {
521             init();
522         }
523         return container;
524     }
525 
526     /**
527      * Creates a body part object.
528      * Can be overridden if you don't want to create a BodyPart.
529      *
530      * @return the created body part
531      */
532     protected BodyPart createBodyPart()
533     {
534         BodyPart bodyPart = new MimeBodyPart();
535         return bodyPart;
536     }
537 
538     /**
539      * Creates a mime multipart object.
540      *
541      * @return the created mime part
542      */
543     protected MimeMultipart createMimeMultipart()
544     {
545         MimeMultipart mmp = new MimeMultipart();
546         return mmp;
547     }
548 
549     /**
550      * Checks whether there are attachments.
551      *
552      * @return true if there are attachments
553      * @since 1.0
554      */
555     public boolean isBoolHasAttachments()
556     {
557         return boolHasAttachments;
558     }
559 
560     /**
561      * Sets whether there are attachments.
562      *
563      * @param b  the attachments flag
564      * @since 1.0
565      */
566     public void setBoolHasAttachments(boolean b)
567     {
568         boolHasAttachments = b;
569     }
570 
571     /**
572      * Checks if this object is initialized.
573      *
574      * @return true if initialized
575      */
576     protected boolean isInitialized()
577     {
578         return initialized;
579     }
580 
581     /**
582      * Sets the initialized status of this object.
583      *
584      * @param b  the initialized status flag
585      */
586     protected void setInitialized(boolean b)
587     {
588         initialized = b;
589     }
590 
591 }