KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > misc > EMailSender


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 /**
66  * EMailSender.java
67  *
68  * Copyright 2001 Jcorporate Ltd.
69  */

70 package com.jcorporate.expresso.core.misc;
71
72 import com.jcorporate.expresso.core.db.DBException;
73 import com.jcorporate.expresso.services.dbobj.Setup;
74 import org.apache.log4j.Logger;
75
76 import javax.activation.DataHandler JavaDoc;
77 import javax.activation.FileDataSource JavaDoc;
78 import javax.mail.Message JavaDoc;
79 import javax.mail.Session JavaDoc;
80 import javax.mail.Transport JavaDoc;
81 import javax.mail.internet.AddressException JavaDoc;
82 import javax.mail.internet.InternetAddress JavaDoc;
83 import javax.mail.internet.MimeBodyPart JavaDoc;
84 import javax.mail.internet.MimeMessage JavaDoc;
85 import javax.mail.internet.MimeMultipart JavaDoc;
86 import java.util.ArrayList JavaDoc;
87 import java.util.Date JavaDoc;
88 import java.util.Iterator JavaDoc;
89 import java.util.List JavaDoc;
90 import java.util.Properties JavaDoc;
91 import java.util.Vector JavaDoc;
92
93 /**
94  * EMailSender is a class that hides the details of sending email through an SMTP server
95  * using the JavaMail API and the Java Activation framework (JAF).
96  * This class logs using the log4j category "expresso.core.misc.EMailSender"
97  * (info, error, debug)
98  * This class uses a DB-context property, mailDebug=Y to dump SMTP debug on the console
99  * <p/>
100  * default number of retries = 0;
101  * <p/>
102  * Creation date: (2/6/01 2:09:18 PM)
103  *
104  * @author Shash Chatterjee
105  */

106 public class EMailSender {
107     private static final String JavaDoc thisClass = EMailSender.class.getName();
108     private static transient final Logger log = Logger.getLogger(EMailSender.class);
109
110     protected String JavaDoc to = null;
111     protected String JavaDoc from = null;
112     protected String JavaDoc personal = null;
113     protected String JavaDoc host = null;
114     protected String JavaDoc user = null;
115     protected String JavaDoc password = null;
116     protected String JavaDoc subject = null;
117     protected String JavaDoc text = null;
118     protected String JavaDoc dbName = null;
119     protected List JavaDoc attachments = null;
120     protected List JavaDoc dataSourceAttachments = null;
121
122     protected boolean htmlFormat = false; // add boolean property for html format emails
123

124     private int maxConnectRetries = 0;
125
126     private long connectRetryDelay = 10000; // 10 sec
127

128     /**
129      * EMailSender constructor
130      */

131     public EMailSender() {
132         super();
133     } /* EMailSender() */
134
135     /**
136      * @return
137      */

138     private String JavaDoc messageDetails() {
139         int aCount = 0;
140         int dsACount = 0;
141
142         if (attachments != null) {
143             aCount = attachments.size();
144         }
145         if (attachments != null) {
146             dsACount = dataSourceAttachments.size();
147         }
148
149         return ("Message Details:\n" + "To '" +
150                 StringUtil.notNull(to) + "'\n" + "From '" +
151                 StringUtil.notNull(from) + "'\n" + "Via host '" +
152                 StringUtil.notNull(host) + "'\n" + "User '" +
153                 StringUtil.notNull(user) + "'\n" + "Subject '" +
154                 StringUtil.notNull(subject) + "'\n" + "Message '" +
155                 StringUtil.notNull(text) + "'\n" + "Db/Context '" +
156                 StringUtil.notNull(dbName) + "'\n" + "With " + aCount +
157                 " file attachments and " + dsACount + " data source attachments.");
158     } /* messageDetails() */
159
160     /**
161      * This method adds an attachment to the email to be sent. Multiple calls can
162      * be made to this method to add multiple attachments
163      *
164      * @param name java.lang.String The fully-qualified name of a file to send as
165      * attachment.
166      */

167     public void addFileAttachment(String JavaDoc name) {
168
169         // Lazy instantiation of the list of filenames, if needed
170
if (attachments == null) {
171             attachments = new ArrayList JavaDoc(4);
172         }
173
174         // Add the attachment to the list of attachments
175
attachments.add(name);
176     } /* addFileAttachments(String) */
177
178     /**
179      * This method provides a Vector of String names of files to be sent as attachments
180      *
181      * @param fileNames java.util.Vector Vector of fully-qualified file-names (String)
182      */

183     public void addFileAttachments(Vector JavaDoc fileNames) {
184
185         // Lazy instantiation of the list of filenames, if needed
186
if (attachments == null) {
187             attachments = new ArrayList JavaDoc(4);
188         }
189         // Loop through each file-name, adding it to the list of files to send
190
for (Iterator JavaDoc itor = fileNames.iterator(); itor.hasNext();) {
191             attachments.add(itor.next());
192         }
193     } /* addFileAttachments(Vector) */
194
195
196     /**
197      * This method adds an attachment to the email to be sent. Multiple calls can
198      * be made to this method to add multiple attachments
199      *
200      * @param dataSource byte array data source object that represents
201      * an virtual attachment with a known content type (MIME type).
202      */

203     public void addDataSourceAttachment(ByteArrayDataSource dataSource) {
204
205         // Lazy instantiation of the list of filenames, if needed
206
if (dataSourceAttachments == null) {
207             dataSourceAttachments = new ArrayList JavaDoc(4);
208         }
209
210         // Add the attachment to the list of attachments
211
dataSourceAttachments.add(dataSource);
212     }
213
214     /**
215      * This method accepts a list of byte array data source collection
216      * to be sent as attachments.
217      *
218      * @param dataSourceList a list of collection of byte array data
219      * source objects that represents an virtual attachment with a
220      * known content type (MIME type).
221      */

222     public void addDataSourceAttachments(List JavaDoc dataSourceList) {
223
224         // Lazy instantiation of the list of filenames, if needed
225
if (attachments == null) {
226             attachments = new ArrayList JavaDoc(4);
227         }
228
229         // Loop through each file-name, adding it to the list of files to send
230
for (Iterator JavaDoc itor = dataSourceList.iterator(); itor.hasNext();) {
231             ByteArrayDataSource dataSource = (ByteArrayDataSource) itor.next();
232             dataSourceAttachments.add(dataSource);
233         }
234     }
235
236     /**
237      * This method returns the name of the DB context, either preset by the caller,
238      * or "default" if not set.
239      *
240      * @return the data context
241      */

242     public String JavaDoc getDBName() {
243         String JavaDoc db = dbName;
244
245         if ((db == null) || (db.trim().equals(""))) {
246             db = "default";
247         }
248
249         dbName = db;
250
251         return db;
252     } /* getDBName() */
253
254     /**
255      * This method returns the email address to be shown in the "From" header of the
256      * email. It will return the address set by the setFromAddress() method. If null,
257      * it will retrieve the from address from the Setup configuration.
258      *
259      * @return internet from address
260      * @throws Exception If a value for MAILFrom is not provided in Setup
261      */

262     private String JavaDoc getFromAddress()
263             throws Exception JavaDoc {
264         String JavaDoc addr = from;
265
266         try {
267             if (addr == null) {
268                 addr = Setup.getValueRequired(getDBName(), "MAILFrom");
269             }
270         } catch (DBException dex) {
271             log.error("DB exception getting MAILFrom:" + messageDetails(), dex);
272             throw dex;
273         }
274
275         from = addr;
276
277         return addr;
278     } /* getFromAddress() */
279
280
281     /**
282      * This method returns the name of the SMTP server to use to send email.
283      * First it returns the value contained in an instance variable. If null,
284      * the val;ue is retrieved from the setting of the MAILServer Setup parameter.
285      *
286      * @return the SMTP host
287      * @throws Exception If MAILServer parameter is not provided in Setup
288      */

289     private String JavaDoc getSMTPHost()
290             throws Exception JavaDoc {
291         String JavaDoc thisMethod = thisClass + "getSMTPHost()";
292         String JavaDoc myHost = StringUtil.notNull(host);
293
294         try {
295             if (myHost.equals("")) {
296                 myHost = Setup.getValueRequired(getDBName(), "MAILServer");
297             }
298         } catch (DBException dex) {
299             log.error("DB exception getting MAILServer:" + messageDetails(),
300                     dex);
301             throw new Exception JavaDoc(thisMethod +
302                     ":DB exception getting MAILServer:" +
303                     dex.getMessage());
304         }
305
306         host = myHost;
307
308         return myHost;
309     } /* getSMTPHost() */
310
311
312     /**
313      * This method returns the SMTP password to use for an authenticated SMTP session
314      * First it returns the value contained in an instance variable. If null,
315      * the value is retrieved from the setting of the MAILPassword Setup parameter.
316      *
317      * @return SMTP password
318      */

319     private String JavaDoc getSMTPPassword() {
320         String JavaDoc myPassword = password;
321
322         try {
323             if (myPassword == null) {
324                 myPassword = Setup.getValueRequired(getDBName(),
325                         "MAILPassword");
326             }
327         } catch (DBException dex) {
328             myPassword = null;
329         }
330
331         password = myPassword;
332
333         return myPassword;
334     } /* getSMTPPassword() */
335
336     /**
337      * This method returns the SMTP username to use for an authenticated SMTP session
338      * First it returns the value contained in an instance variable. If null,
339      * the value is retrieved from the setting of the MAILUserName Setup parameter.
340      *
341      * @return SMTP username
342      */

343     private String JavaDoc getSMTPUser() {
344         String JavaDoc myUser = StringUtil.notNull(user);
345
346         try {
347             if (myUser.equals("")) {
348                 myUser = Setup.getValueRequired(getDBName(), "MAILUserName");
349             }
350         } catch (DBException dex) {
351             myUser = null;
352         }
353
354         user = myUser;
355
356         return myUser;
357     } /* getSMTPUser() */
358
359     /**
360      * This method does all the dirty work of actually sending the email.
361      *
362      * @throws Exception if the internet mailing service fails
363      */

364     public void send() throws Exception JavaDoc {
365         // The From header value
366
String JavaDoc myFrom = getFromAddress();
367
368         // The SMTP server name
369
String JavaDoc myHost = getSMTPHost();
370
371         // The SMTP user name - if null, authenticated session is not used
372
String JavaDoc myUser = getSMTPUser();
373
374         // The SMTP password
375
String JavaDoc myPassword = getSMTPPassword();
376
377         String JavaDoc myPersonal = getPersonal();
378
379         // To use authenticated SMTP session, or not
380
boolean useAuth = false;
381         StringUtil.assertNotBlank(myHost,
382                 "Host cannot be blank when sending email");
383
384         if (log.isDebugEnabled()) {
385             log.debug("Sending email:" + messageDetails());
386         }
387         // Make sure we have a "@" in the email address...this filters addresses like "None" etc.
388
if (to.indexOf("@") == -1) {
389             log.warn("Invalid to address, does not contain \"@\" - " + to);
390
391             //Don't thorw exception .... simply log and return...
392
return;
393         }
394         // If SMTP username is null or empty, use a "plain" non-authenticated
395
// SMTP sessions
396
if (myUser != null && !myUser.trim().equals("")) {
397             useAuth = true;
398         }
399
400         Session JavaDoc session = null;
401         Transport JavaDoc trans = null;
402         InternetAddress JavaDoc[] fromAddr = null;
403         InternetAddress JavaDoc[] toAddr = null;
404
405         // Parse out the From address for name, email, etc.
406
try {
407             fromAddr = InternetAddress.parse(myFrom, false);
408         } catch (AddressException JavaDoc aex) {
409             log.error("Invalid from address - '" + from + "':" + messageDetails(), aex);
410             throw aex;
411         }
412         // Parse out the To address for name, email etc.
413
try {
414             toAddr = InternetAddress.parse(to, false);
415         } catch (AddressException JavaDoc aex) {
416             log.error("Invalid to address - '" + to + "':" + messageDetails(), aex);
417             throw aex;
418         }
419
420         //***********************************************************************
421
// Create a session
422
//***********************************************************************
423
// Setup properties for the SMTP session
424
Properties JavaDoc props = new Properties JavaDoc();
425         props.put("mail.smtp.host", myHost); // The name of the SMTP server
426

427         try {
428             if (useAuth) {
429
430                 // Must use an authenticated session
431
if (log.isDebugEnabled()) {
432                     log.debug("Setting up secure email session, host=" +
433                             myHost + " user=" + myUser);
434                 }
435
436                 props.put("mail.smtp.auth", "true");
437
438                 EMailAuthenticator auth = new EMailAuthenticator();
439
440                 // Force the user/password values, otherwise EMailAuthenticator will
441
// attempt to look them up
442
auth.setUser(myUser);
443                 auth.setPassword(myPassword);
444
445                 // Create the session
446
session = Session.getInstance(props, auth);
447             } else {
448
449                 // Create a "normal" session
450
if (log.isDebugEnabled()) {
451                     log.debug("Setting up normal email session, host=" +
452                             myHost);
453                 }
454
455                 session = Session.getInstance(props, null);
456             }
457             // Prints debug output to the console if "mailDebug=Y" in DB context
458
// properties file
459
if (ConfigManager.getContext(getDBName()).mailDebug()) {
460                 session.setDebug(true);
461             }
462         } catch (Exception JavaDoc e) {
463             log.error("Unable to create mail session:" + messageDetails(), e);
464             throw e;
465         }
466
467         //***********************************************************************
468
// Format the message and attachments
469
//***********************************************************************
470
// Create a container for the entire message
471
MimeMessage JavaDoc msg = null;
472
473         try {
474
475             // The entire container is a MIME messgae
476
msg = new MimeMessage JavaDoc(session);
477
478             // set the Personal value
479
fromAddr[0].setPersonal(myPersonal);
480             // Setup headers
481
msg.setFrom(fromAddr[0]); // The From header
482
msg.setRecipients(Message.RecipientType.TO, toAddr); // The To header
483
msg.setSubject(subject); // The Subject header
484
msg.setSentDate(new Date JavaDoc()); // The Date header
485

486             // A message contains a multi-part MIME container
487
// The multi-part MIME container contains multiple MIME body parts
488
MimeMultipart JavaDoc mp = new MimeMultipart JavaDoc();
489
490             // The first body part is the text message - create and add it to the
491
// multi-part
492
MimeBodyPart JavaDoc mbp = new MimeBodyPart JavaDoc();
493             mbp.setText(text);
494             if (htmlFormat) {
495                 mbp.setContent(text, "text/html"); //Set HTML format if desired
496
}
497
498             mp.addBodyPart(mbp);
499
500             // Iterate through each attachment name, creating a body part for each
501
// and attaching the
502
// body part to the multi-part
503
String JavaDoc oneFileName = null;
504
505             if (attachments != null) {
506                 // Take care of the filename attachments. We must
507
// create a FileDataSource for each filename in the
508
// list collection int order create MIME body parts.
509
//
510
for (Iterator JavaDoc itor = attachments.iterator(); itor.hasNext();) {
511                     oneFileName = (String JavaDoc) itor.next();
512
513                     MimeBodyPart JavaDoc mbp2 = new MimeBodyPart JavaDoc();
514                     FileDataSource JavaDoc fds = new FileDataSource JavaDoc(oneFileName);
515                     mbp2.setDataHandler(new DataHandler JavaDoc(fds));
516                     mbp2.setFileName(fds.getName());
517                     mp.addBodyPart(mbp2);
518                 }
519             }
520
521             if (dataSourceAttachments != null) {
522                 // Take care of the byte array data source
523
// attachments. We only need to create the MIME body
524
// part from the data source.
525
for (Iterator JavaDoc itor = dataSourceAttachments.iterator(); itor.hasNext();) {
526                     ByteArrayDataSource bads = (ByteArrayDataSource) itor.next();
527
528                     MimeBodyPart JavaDoc mbp3 = new MimeBodyPart JavaDoc();
529                     mbp3.setDataHandler(new DataHandler JavaDoc(bads));
530                     mbp3.setFileName(bads.getName());
531                     mp.addBodyPart(mbp3);
532                 }
533             }
534
535             // Finally add the multi-part to the MIME message
536
msg.setContent(mp);
537         } catch (Exception JavaDoc e) {
538             log.error("Error in creating MIME message:" + messageDetails(), e);
539             throw e;
540         }
541         //***********************************************************************
542
// Now send the message
543
//***********************************************************************
544
try {
545
546             // Get the transport from the session
547
trans = session.getTransport(toAddr[0]);
548
549             // Connect to the SMTP server using the SMTP transport
550
int connectRetries = 0;
551             do { // try once, then start counting retries up to max
552
try {
553                     trans.connect();
554                 } catch (Exception JavaDoc e) {
555                     if (e.getMessage().equals("already connected")) {
556                         break;
557                     }
558                     if (connectRetries >= maxConnectRetries) {
559                         throw e;
560                     } else {
561                         log.debug("Retry (wait="
562                                 + connectRetryDelay
563                                 + "ms) on SMTP connect, count="
564                                 + connectRetries
565                                 + ", error="
566                                 + e.getMessage()
567                                 + ", detail="
568                                 + messageDetails());
569                         Thread.sleep(connectRetryDelay);
570                     }
571                 }
572             } while (connectRetries++ < maxConnectRetries);
573
574
575
576             // Actually send the message
577
trans.sendMessage(msg, toAddr);
578
579             // Close the connection
580
trans.close();
581         } catch (Exception JavaDoc e) {
582             log.error("Error sending email:" + messageDetails(), e);
583             throw e;
584         }
585     } /* send() */
586
587
588     /**
589      * This is a convinience method to wrap the send() method.
590      *
591      * @param to java.lang.String The address to send email to
592      * @param subject java.lang.String The subject of the message
593      * @param text java.lang.String The text of the message to send
594      * @throws Exception Propagated exception from the send() method
595      */

596     public void send(String JavaDoc to, String JavaDoc subject, String JavaDoc text)
597             throws Exception JavaDoc {
598         setToAddress(to);
599         setSubject(subject);
600         setText(text);
601         send();
602     } /* send(String, String, String) */
603
604
605     /**
606      * This method sets the DB context name, used for looking up several Setup parameters
607      *
608      * @param newDBName String Name of DB context
609      */

610     public void setDBName(String JavaDoc newDBName) {
611         dbName = StringUtil.notNull(newDBName);
612
613         if (dbName.equals("")) {
614             dbName = "default";
615         }
616     } /* setDBName(String) */
617
618     /**
619      * This method sets the address to be used in the From header
620      *
621      * @param from java.lang.String The from address
622      */

623     public void setFromAddress(String JavaDoc from) {
624         this.from = from;
625     } /* setFromAddress(String) */
626
627
628     /**
629      * Tells the EMail sender to send HTML mail only
630      */

631     public void setEmailHtmlFormat() {
632         // call this when EMailSender object is created
633
htmlFormat = true;
634     }
635
636     /**
637      * This method sets the address to be used in the From header
638      *
639      * @param from java.lang.String The from address
640      */

641     public void setPersonal(String JavaDoc personal) {
642         this.personal = personal;
643     } /* setFromAddress(String) */
644
645     /**
646      * This method sets the address to be used in the From header
647      *
648      * @param from java.lang.String The from address
649      */

650     public String JavaDoc getPersonal() {
651         if (personal == null) {
652             return new String JavaDoc("");
653         } else {
654             return this.personal;
655         }
656     } /* setFromAddress(String) */
657
658
659     /**
660      * This method sets the name of the SMTP server to use
661      *
662      * @param host java.lang.String Name of SMTP server
663      */

664     public void setSMTPHost(String JavaDoc host) {
665         this.host = host;
666     } /* setSMTPHost(String) */
667
668     /**
669      * This method sets the SMTP password to use for authentication
670      *
671      * @param password java.lang.String SMTP password to use for authentication
672      */

673     public void setSMTPPassword(String JavaDoc password) {
674         this.password = password;
675     } /* setSMTPPassword(String) */
676
677     /**
678      * This method sets the username to use for SMTP authentication
679      *
680      * @param user java.lang.String Username for SMTP authentication
681      */

682     public void setSMTPUser(String JavaDoc user) {
683         this.user = user;
684     } /* setSMTPUser(String) */
685
686     /**
687      * This method sets the value to use for the Subject header
688      *
689      * @param subject java.lang.String The subject header contents
690      */

691     public void setSubject(String JavaDoc subject) {
692         this.subject = subject;
693     } /* setSubject(String) */
694
695     /**
696      * Thiis method sets the content string of the email message
697      *
698      * @param text java.lang.String Email message content
699      */

700     public void setText(String JavaDoc text) {
701         this.text = text;
702     } /* setText(String) */
703
704     /**
705      * This method sets the address to be used in the To header
706      *
707      * @param to java.lang.String The address to use in To header
708      */

709     public void setToAddress(String JavaDoc to) {
710         this.to = to;
711     } /* setToAddress(String) */
712
713     /**
714      * Sets the connectRetryDelay.
715      *
716      * @param connectRetryDelay The connectRetryDelay to set
717      */

718     public void setConnectRetryDelay(long connectRetryDelay) {
719         this.connectRetryDelay = connectRetryDelay;
720     }
721
722     /**
723      * Sets the maxConnectRetries.
724      *
725      * @param maxConnectRetries The maxConnectRetries to set
726      */

727     public void setMaxConnectRetries(int maxConnectRetries) {
728         this.maxConnectRetries = maxConnectRetries;
729     }
730
731 } /* EMailSender */
732
Popular Tags