KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > util > EmailService


1 /**
2  * $RCSfile: EmailService.java,v $
3  * $Revision: 1.5 $
4  * $Date: 2005/06/15 01:21:03 $
5  *
6  * Copyright (C) 2003-2005 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.util;
13
14 import java.security.Security JavaDoc;
15 import java.text.SimpleDateFormat JavaDoc;
16 import java.util.*;
17 import java.util.concurrent.ThreadPoolExecutor JavaDoc;
18 import java.util.concurrent.TimeUnit JavaDoc;
19 import java.util.concurrent.ArrayBlockingQueue JavaDoc;
20
21 import javax.mail.Address JavaDoc;
22 import javax.mail.*;
23 import javax.mail.internet.*;
24
25 /**
26  * A service to send email.<p>
27  *
28  * This class has a few factory methods you can use to return message objects
29  * or to add messages into a queue to be sent. Using these methods, you can
30  * send emails in the following couple of ways:<p>
31  * <pre>
32  * EmailTask emailTask = new EmailTask();
33  * emailTask.addMessage(
34  * "Joe Bloe", "jbloe@place.org",
35  * "Jane Doe", "jane@doe.com",
36  * "Hello...",
37  * "This is the body of the email..."
38  * );
39  * emailTask.run();
40  * </pre>
41  * or
42  * <pre>
43  * EmailTask emailTask = new EmailTask();
44  * Message message = emailTask.createMimeMessage();
45  * // call setters on the message object
46  * // .
47  * // .
48  * // .
49  * emailTask.sendMessage(message);
50  * emailTask.run();
51  * </pre><p>
52  *
53  * This class is configured with a set of Jive properties:<ul>
54  * <li><tt>mail.smtp.host</tt> -- the host name of your mail server, i.e.
55  * mail.yourhost.com. The default value is "localhost".
56  * <li><tt>mail.smtp.port</tt> -- an optional property to change the smtp
57  * port used from the default of 25.
58  * <li><tt>mail.smtp.username</tt> -- an optional property to change the
59  * username used to connect to the smtp server. Default is no username.
60  * <li><tt>mail.smtp.password</tt> -- an optional property to change the
61  * password used to connect to the smtp server. Default is no password.
62  * <li><tt>mail.smtp.ssl</tt> -- an optional property to set whether to use
63  * SSL to connect to the smtp server or not. Default is false.
64  * </ul>
65  */

66 public class EmailService {
67
68     private static final String JavaDoc SSL_FACTORY = "org.jivesoftware.util.SimpleSSLSocketFactory";
69
70     private static EmailService instance = new EmailService();
71
72     public static EmailService getInstance() {
73         return instance;
74     }
75
76     private String JavaDoc host;
77     private int port;
78     private String JavaDoc username;
79     private String JavaDoc password;
80     private boolean sslEnabled;
81     private boolean debugEnabled;
82
83     private ThreadPoolExecutor JavaDoc executor;
84     private Session session = null;
85
86     /**
87      * Constructs a new EmailService instance.
88      */

89     private EmailService() {
90         executor = new ThreadPoolExecutor JavaDoc(1, Integer.MAX_VALUE, 60,
91             TimeUnit.SECONDS, new ArrayBlockingQueue JavaDoc<Runnable JavaDoc>(5));
92
93         host = JiveGlobals.getProperty("mail.smtp.host", "localhost");
94         port = JiveGlobals.getIntProperty("mail.smtp.port", 25);
95         username = JiveGlobals.getProperty("mail.smtp.username");
96         password = JiveGlobals.getProperty("mail.smtp.password");
97         sslEnabled = JiveGlobals.getBooleanProperty("mail.smtp.ssl");
98         debugEnabled = JiveGlobals.getBooleanProperty("mail.debug");
99     }
100
101     /**
102      * Factory method to return a blank JavaMail message. You should use the
103      * object returned and set desired message properties. When done, pass the
104      * object to the addMessage(Message) method.
105      *
106      * @return A new JavaMail message.
107      */

108     public MimeMessage createMimeMessage() {
109         if (session == null) {
110             createSession();
111         }
112         return new MimeMessage(session);
113     }
114
115     /**
116      * Sends a JavaMail message. To create a message, use the
117      * {@link #createMimeMessage()} method.
118      *
119      * @param message the message to send.
120      */

121     public void sendMessage(MimeMessage message) {
122         if (message != null) {
123             sendMessages(Collections.singletonList(message));
124         }
125         else {
126             Log.error("Cannot add null email message to queue.");
127         }
128     }
129
130     /**
131      * Send a collection of messages. To create a message, use the
132      * {@link #createMimeMessage()} method.
133      *
134      * @param messages a collection of the messages to send.
135      */

136     public void sendMessages(Collection<MimeMessage> messages) {
137         // If there are no messages, do nothing.
138
if (messages.size() == 0) {
139             return;
140         }
141         executor.execute(new EmailTask(messages));
142     }
143
144     /**
145      * Sends a message, specifying all of its fields.<p>
146      *
147      * To have more advanced control over the message sent, use the
148      * {@link #sendMessage(MimeMessage)} method.<p>
149      *
150      * Both a plain text and html body can be specified. If one of the values is null,
151      * only the other body type is sent. If both body values are set, a multi-part
152      * message will be sent. If parts of the message are invalid (ie, the toEmail is null)
153      * the message won't be sent.
154      *
155      * @param toName the name of the recipient of this email.
156      * @param toEmail the email address of the recipient of this email.
157      * @param fromName the name of the sender of this email.
158      * @param fromEmail the email address of the sender of this email.
159      * @param subject the subject of the email.
160      * @param textBody plain text body of the email, which can be <tt>null</tt> if the
161      * html body is not null.
162      * @param htmlBody html body of the email, which can be <tt>null</tt> if the text body
163      * is not null.
164      */

165     public void sendMessage(String JavaDoc toName, String JavaDoc toEmail, String JavaDoc fromName,
166             String JavaDoc fromEmail, String JavaDoc subject, String JavaDoc textBody, String JavaDoc htmlBody)
167     {
168         // Check for errors in the given fields:
169
if (toEmail == null || fromEmail == null || subject == null ||
170                 (textBody == null && htmlBody == null))
171         {
172             Log.error("Error sending email: Invalid fields: "
173                     + ((toEmail == null) ? "toEmail " : "")
174                     + ((fromEmail == null) ? "fromEmail " : "")
175                     + ((subject == null) ? "subject " : "")
176                     + ((textBody == null && htmlBody == null) ? "textBody or htmlBody " : "")
177             );
178         }
179         else {
180             try {
181                 String JavaDoc encoding = MimeUtility.mimeCharset("iso-8859-1");
182                 MimeMessage message = createMimeMessage();
183                 Address JavaDoc to = null;
184                 Address JavaDoc from = null;
185
186                 if (toName != null) {
187                     to = new InternetAddress(toEmail, toName, encoding);
188                 }
189                 else {
190                     to = new InternetAddress(toEmail, "", encoding);
191                 }
192
193                 if (fromName != null) {
194                     from = new InternetAddress(fromEmail, fromName, encoding);
195                 }
196                 else {
197                     from = new InternetAddress(fromEmail, "", encoding);
198                 }
199
200                 // Set the date of the message to be the current date
201
SimpleDateFormat JavaDoc format = new SimpleDateFormat JavaDoc("EEE, dd MMM yyyy HH:mm:ss Z",
202                         java.util.Locale.US);
203                 format.setTimeZone(JiveGlobals.getTimeZone());
204                 message.setHeader("Date", format.format(new Date()));
205                 message.setHeader("Content-Transfer-Encoding", "8bit");
206                 message.setRecipient(Message.RecipientType.TO, to);
207                 message.setFrom(from);
208                 message.setSubject(StringUtils.replace(subject, "\n", ""), encoding);
209                 // Create HTML, plain-text, or combination message
210
if (textBody != null && htmlBody != null) {
211                     MimeMultipart content = new MimeMultipart("alternative");
212                     // Plain-text
213
MimeBodyPart text = new MimeBodyPart();
214                     text.setText(textBody, encoding);
215                     text.setDisposition(Part.INLINE);
216                     content.addBodyPart(text);
217                     // HTML
218
MimeBodyPart html = new MimeBodyPart();
219                     html.setContent(htmlBody, "text/html");
220                     html.setDisposition(Part.INLINE);
221                     content.addBodyPart(html);
222                     // Add multipart to message.
223
message.setContent(content);
224                     message.setDisposition(Part.INLINE);
225                     sendMessage(message);
226                 }
227                 else if (textBody != null) {
228                     MimeBodyPart bPart = new MimeBodyPart();
229                     bPart.setText(textBody, encoding);
230                     bPart.setDisposition(Part.INLINE);
231                     MimeMultipart mPart = new MimeMultipart();
232                     mPart.addBodyPart(bPart);
233                     message.setContent(mPart);
234                     message.setDisposition(Part.INLINE);
235                     // Add the message to the send list
236
sendMessage(message);
237                 }
238                 else if (htmlBody != null) {
239                     MimeBodyPart bPart = new MimeBodyPart();
240                     bPart.setContent(htmlBody, "text/html");
241                     bPart.setDisposition(Part.INLINE);
242                     MimeMultipart mPart = new MimeMultipart();
243                     mPart.addBodyPart(bPart);
244                     message.setContent(mPart);
245                     message.setDisposition(Part.INLINE);
246                     // Add the message to the send list
247
sendMessage(message);
248                 }
249             }
250             catch (Exception JavaDoc e) {
251                 Log.error(e);
252             }
253         }
254     }
255
256     /**
257      * Sends a collection of email messages. This method differs from
258      * {@link #sendMessages(Collection<MimeMessage>)} in that messages are sent
259      * before this method returns rather than queueing the messages to be sent later.
260      *
261      * @param messages
262      * @throws MessagingException
263      */

264     public void sendMessagesImmediately(Collection<MimeMessage> messages)
265             throws MessagingException
266     {
267         EmailTask task = new EmailTask(messages);
268         task.sendMessages();
269     }
270
271     /**
272      * Returns the SMTP host (e.g. mail.example.com). The default value is "localhost".
273      *
274      * @return the SMTP host.
275      */

276     public String JavaDoc getHost() {
277         return host;
278     }
279
280     /**
281      * Sets the SMTP host (e.g. mail.example.com). The default value is "localhost".
282      *
283      * @param host the SMTP host.
284      */

285     public void setHost(String JavaDoc host) {
286         this.host = host;
287         JiveGlobals.setProperty("mail.smtp.host", host);
288         session = null;
289     }
290
291     /**
292      * Returns the port number used when connecting to the SMTP server. The default
293      * port is 25.
294      *
295      * @return the SMTP port.
296      */

297     public int getPort() {
298         return port;
299     }
300
301     /**
302      * Sets the port number that will be used when connecting to the SMTP
303      * server. The default is 25, the standard SMTP port number.
304      *
305      * @param port the SMTP port number.
306      */

307     public void setPort(int port) {
308         if (port < 0) {
309             throw new IllegalArgumentException JavaDoc("Invalid port value: " + port);
310         }
311         this.port = port;
312         JiveGlobals.setProperty("mail.smtp.port", Integer.toString(port));
313         session = null;
314     }
315
316     /**
317      * Returns the username used to connect to the SMTP server. If the username
318      * is <tt>null</tt>, no username will be used when connecting to the server.
319      *
320      * @return the username used to connect to the SMTP server, or <tt>null</tt> if
321      * there is no username.
322      */

323     public String JavaDoc getUsername() {
324         return username;
325     }
326
327     /**
328      * Sets the username that will be used when connecting to the SMTP
329      * server. The default is <tt>null</tt>, or no username.
330      *
331      * @param username the SMTP username.
332      */

333     public void setUsername(String JavaDoc username) {
334         this.username = username;
335         if (username == null) {
336             JiveGlobals.deleteProperty("mail.smtp.username");
337         }
338         else {
339             JiveGlobals.setProperty("mail.smtp.username", username);
340         }
341         session = null;
342     }
343
344     /**
345      * Returns the password used to connect to the SMTP server. If the password
346      * is <tt>null</tt>, no password will be used when connecting to the server.
347      *
348      * @return the password used to connect to the SMTP server, or <tt>null</tt> if
349      * there is no password.
350      */

351     public String JavaDoc getPassword() {
352         return password;
353     }
354
355     /**
356      * Sets the password that will be used when connecting to the SMTP
357      * server. The default is <tt>null</tt>, or no password.
358      *
359      * @param password the SMTP password.
360      */

361     public void setPassword(String JavaDoc password) {
362         this.password = password;
363         if (password == null) {
364             JiveGlobals.deleteProperty("mail.smtp.password");
365         }
366         else {
367             JiveGlobals.setProperty("mail.smtp.password", password);
368         }
369         session = null;
370     }
371
372     /**
373      * Returns true if SMTP debugging is enabled. Debug information is
374      * written to <tt>System.out</tt> by the underlying JavaMail provider.
375      *
376      * @return true if SMTP debugging is enabled.
377      */

378     public boolean isDebugEnabled() {
379         return debugEnabled;
380     }
381
382     /**
383      * Enables or disables SMTP transport layer debugging. Debug information is
384      * written to <tt>System.out</tt> by the underlying JavaMail provider.
385      *
386      * @param debugEnabled true if SMTP debugging should be enabled.
387      */

388     public void setDebugEnabled(boolean debugEnabled) {
389         this.debugEnabled = debugEnabled;
390         JiveGlobals.setProperty("mail.debug", Boolean.toString(debugEnabled));
391         session = null;
392     }
393
394     /**
395      * Returns true if SSL is enabled for SMTP connections.
396      *
397      * @return true if SSL is enabled.
398      */

399     public boolean isSSLEnabled() {
400         return sslEnabled;
401     }
402
403     /**
404      * Sets whether the SMTP connection is configured to use SSL or not.
405      * Typically, the port should be 465 when using SSL with SMTP.
406      *
407      * @param sslEnabled true if ssl should be enabled, false otherwise.
408      */

409     public void setSSLEnabled(boolean sslEnabled) {
410         this.sslEnabled = sslEnabled;
411         JiveGlobals.setProperty("mail.smtp.ssl", Boolean.toString(sslEnabled));
412         session = null;
413     }
414
415     /**
416      * Creates a Javamail session.
417      */

418     private synchronized void createSession() {
419         if (host == null) {
420             throw new IllegalArgumentException JavaDoc("Host cannot be null.");
421         }
422
423         Properties mailProps = new Properties();
424         mailProps.setProperty("mail.smtp.host", host);
425         mailProps.setProperty("mail.smtp.port", String.valueOf(port));
426         // Allow messages with a mix of valid and invalid recipients to still be sent.
427
mailProps.setProperty("mail.smtp.sendpartial", "true");
428         mailProps.setProperty("mail.debug", String.valueOf(debugEnabled));
429
430         // Methology from an article on www.javaworld.com (Java Tip 115)
431
// We will attempt to failback to an insecure connection
432
// if the secure one cannot be made
433
if (sslEnabled) {
434             // Register with security provider.
435
Security.setProperty("ssl.SocketFactory.provider", SSL_FACTORY);
436
437             mailProps.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
438             mailProps.setProperty("mail.smtp.socketFactory.fallback", "true");
439         }
440
441         // If a username is defined, use SMTP authentication.
442
if (username != null) {
443             mailProps.put("mail.smtp.auth", "true");
444         }
445         session = Session.getInstance(mailProps, null);
446     }
447
448     /**
449      * Task to send one or more emails via the SMTP server.
450      */

451     private class EmailTask implements Runnable JavaDoc {
452
453         private Collection<MimeMessage> messages;
454
455         public EmailTask(Collection<MimeMessage> messages) {
456             this.messages = messages;
457         }
458
459         public void run() {
460             try {
461                 sendMessages();
462             }
463             catch (MessagingException me) {
464                 Log.error(me);
465             }
466         }
467
468         public void sendMessages() throws MessagingException {
469             Transport transport = null;
470             try {
471                 URLName url = new URLName("smtp", host, port, "", username, password);
472                 if (session == null) {
473                     createSession();
474                 }
475                 transport = new com.sun.mail.smtp.SMTPTransport(session, url);
476                 transport.connect(host, port, username, password);
477                 for (MimeMessage message : messages) {
478                     // Attempt to send message, but catch exceptions caused by invalid
479
// addresses so that other messages can continue to be sent.
480
try {
481                         transport.sendMessage(message,
482                             message.getRecipients(MimeMessage.RecipientType.TO));
483                     }
484                     catch (AddressException ae) {
485                         Log.error(ae);
486                     }
487                     catch (SendFailedException sfe) {
488                         Log.error(sfe);
489                     }
490                 }
491             }
492             finally {
493                 if (transport != null) {
494                     try {
495                         transport.close();
496                     }
497                     catch (MessagingException e) { /* ignore */ }
498                 }
499             }
500         }
501     }
502 }
Popular Tags