KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > smtphandler > SMTPHandler


1 /*
2  * ============================================================================
3  * The Apache Software License, Version 1.1
4  * ============================================================================
5  *
6  * Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without modifica-
9  * tion, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * 3. The end-user documentation included with the redistribution, if any, must
19  * include the following acknowledgment: "This product includes software
20  * developed by the Apache Software Foundation (http://www.apache.org/)."
21  * Alternately, this acknowledgment may appear in the software itself, if
22  * and wherever such third-party acknowledgments normally appear.
23  *
24  * 4. The names "log4j" and "Apache Software Foundation" must not be used to
25  * endorse or promote products derived from this software without prior
26  * written permission. For written permission, please contact
27  * apache@apache.org.
28  *
29  * 5. Products derived from this software may not be called "Apache", nor may
30  * "Apache" appear in their name, without prior written permission of the
31  * Apache Software Foundation.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
34  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
36  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
38  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
39  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
40  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
42  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43  *
44  * This software consists of voluntary contributions made by many individuals
45  * on behalf of the Apache Software Foundation. For more information on the
46  * Apache Software Foundation, please see <http://www.apache.org/>.
47  *
48  */

49
50 package smtphandler;
51
52 import java.util.logging.*;
53
54 import java.util.Date JavaDoc;
55 import java.util.Properties JavaDoc;
56
57 import javax.mail.Authenticator JavaDoc;
58 import javax.mail.Message JavaDoc;
59 import javax.mail.Multipart JavaDoc;
60 import javax.mail.Session JavaDoc;
61 import javax.mail.Transport JavaDoc;
62 import javax.mail.internet.AddressException JavaDoc;
63 import javax.mail.internet.InternetAddress JavaDoc;
64 import javax.mail.internet.MimeBodyPart JavaDoc;
65 import javax.mail.internet.MimeMessage JavaDoc;
66 import javax.mail.internet.MimeMultipart JavaDoc;
67
68 /**
69    Send an e-mail when a specific logging record occurs, typically on
70    {@link java.util.logging.Level#WARNING WARNING}
71    or {@link java.util.logging.Level#SEVERE SEVERE}
72
73    <p>The number of {@link LogRecord} objects delivered in this e-mail depend on
74    the value of <b>bufferSize</b> option. The
75    <code>SMTPHandler</code> keeps only the last
76    <code>bufferSize</code> records in its cyclic buffer. This
77    keeps memory requirements at a reasonable level while still
78    delivering useful application context.</p>
79
80    <p>
81    <img SRC="doc-files/smtphandler-diagram.jpg">
82    </p>
83
84    <p>
85    The code in this class is derived from
86    <a HREF="http://logging.apache.org/log4j/">log4j</a>'s
87    SMTPAppender class.
88    </p>
89    
90    @author Ceki G&uuml;lc&uuml; (author of log4j's SMTPAppender)
91    @author Sean C. Sullivan
92
93    */

94 public class SMTPHandler extends java.util.logging.Handler JavaDoc
95 {
96     static private final int DEFAULT_ERROR_CODE = 1;
97     static private final Formatter DEFAULT_FORMATTER = new SimpleFormatter();
98     static private final Level DEFAULT_LEVEL = Level.WARNING;
99     static private final int DEFAULT_BUFFER_SIZE = 512;
100     static private final String JavaDoc name = SMTPHandler.class.getName();
101     private String JavaDoc to;
102     private String JavaDoc from;
103     private String JavaDoc subject;
104     private String JavaDoc smtpHost;
105     private String JavaDoc smtpUsername;
106     private String JavaDoc smtpPassword;
107     private int bufferSize = DEFAULT_BUFFER_SIZE;
108     protected CyclicBuffer cb = new CyclicBuffer(bufferSize);
109     protected TriggeringRecordEvaluator evaluator;
110
111     public SMTPHandler()
112     {
113         final String JavaDoc prefix = SMTPHandler.class.getName();
114
115         LogManager manager = LogManager.getLogManager();
116          
117         String JavaDoc strLevel = manager.getProperty(prefix + ".level");
118         Level lev;
119         if (strLevel == null)
120         {
121             lev = DEFAULT_LEVEL;
122         }
123         try
124         {
125             lev = Level.parse(strLevel);
126         }
127         catch (Exception JavaDoc ex)
128         {
129             lev = DEFAULT_LEVEL;
130         }
131         setLevel(lev);
132
133         String JavaDoc strFormatterClassName = manager.getProperty(prefix + ".formatter");
134         Formatter f = (Formatter) instantiateByClassName(strFormatterClassName,
135                         DEFAULT_FORMATTER);
136         this.setFormatter(f);
137
138
139         setTo(manager.getProperty(prefix + ".to"));
140         setFrom(manager.getProperty(prefix + ".from"));
141         setSmtpHost(manager.getProperty(prefix + ".smtpHost"));
142         setSmtpUsername(manager.getProperty(prefix + ".smtpUsername"));
143         setSmtpPassword(manager.getProperty(prefix + ".smtpPassword"));
144         setSubject(manager.getProperty(prefix + ".subject"));
145
146         String JavaDoc strEvaluatorClassName = manager.getProperty(prefix + ".evaluatorClass");
147         this.setEvaluatorClass(strEvaluatorClassName);
148         
149         try
150         {
151             String JavaDoc strBufSize = manager.getProperty(prefix + ".bufferSize");
152             setBufferSize(Integer.parseInt(strBufSize));
153         }
154         catch (Exception JavaDoc ex)
155         {
156             setBufferSize(DEFAULT_BUFFER_SIZE);
157         }
158
159     }
160
161
162     /**
163        Perform SMTPHandler specific appending actions, mainly adding
164        the record to a cyclic buffer and checking if the record triggers
165        an e-mail to be sent. */

166     public synchronized void publish(LogRecord record)
167     {
168         if (record == null)
169         {
170             return;
171         }
172         else if ( ! isLoggable(record))
173         {
174             return;
175         }
176         else if (!checkEntryConditions())
177         {
178             return;
179         }
180
181         cb.add(record);
182
183         if (evaluator.isTriggeringRecord(record))
184         {
185             sendBuffer();
186         }
187     }
188
189     /**
190         This method determines if there is a sense in attempting to append.
191     
192         <p>It checks whether there is a "To" address set and also if
193         there is a set evaluator. If these checks fail, then the boolean
194         value <code>false</code> is returned. */

195     protected boolean checkEntryConditions()
196     {
197         if (this.evaluator == null)
198         {
199             reportError(
200                 "No TriggeringRecordEvaluator is set for handler ["
201                     + name
202                     + "].",
203                 null,
204                 DEFAULT_ERROR_CODE);
205         
206             return false;
207         }
208         
209         if (getTo() == null)
210         {
211             reportError(
212                 "No 'To' email address set for handler ["
213                     + name
214                     + "].",
215                 null,
216                 DEFAULT_ERROR_CODE);
217             return false;
218         
219         }
220
221         return true;
222     }
223
224     public synchronized void close()
225     {
226         // todo this.closed = true;
227
}
228
229     protected InternetAddress JavaDoc getAddress(String JavaDoc addressStr)
230     {
231         try
232         {
233             return new InternetAddress JavaDoc(addressStr);
234         }
235         catch (AddressException JavaDoc e)
236         {
237             reportError("Could not parse address [" + addressStr + "].",
238                 e,
239                 DEFAULT_ERROR_CODE);
240
241             return null;
242         }
243     }
244
245     protected InternetAddress JavaDoc[] parseAddress(String JavaDoc addressStr)
246     {
247         try
248         {
249             return InternetAddress.parse(addressStr, true);
250         }
251         catch (AddressException JavaDoc e)
252         {
253             reportError("Could not parse address [" + addressStr + "].",
254                     e,
255                     DEFAULT_ERROR_CODE);
256
257             return new InternetAddress JavaDoc[0];
258         }
259     }
260
261     /**
262        Returns value of the <b>To</b> option.
263      */

264     public String JavaDoc getTo()
265     {
266         return to;
267     }
268
269     /**
270        Send the contents of the cyclic buffer as an e-mail message.
271      */

272     protected void sendBuffer()
273     {
274         // Note: this code already owns the monitor for this
275
// handler. This frees us from needing to synchronize on 'cb'.
276

277         Properties JavaDoc props = new Properties JavaDoc(System.getProperties());
278
279         if (smtpHost != null)
280         {
281             props.put("mail.smtp.host", smtpHost);
282         }
283
284         Authenticator JavaDoc auth = null;
285         
286         if (this.getSmtpUsername() != null)
287         {
288             auth = new UsernamePasswordAuthenticator(
289                                         this.getSmtpUsername(),
290                                         this.getSmtpPassword());
291             props.put("mail.smtp.user", this.getSmtpUsername());
292             props.put("mail.smtp.auth", "true");
293         }
294         
295         Session JavaDoc session = Session.getInstance(props, auth);
296
297         session.setDebug(true);
298
299         MimeMessage JavaDoc msg = new MimeMessage JavaDoc(session);
300
301         try
302         {
303             if (from != null)
304             {
305                 msg.setFrom(getAddress(from));
306             }
307             else
308             {
309                 msg.setFrom();
310             }
311
312             msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
313
314             if (subject != null)
315             {
316                 msg.setSubject(subject);
317             }
318
319             MimeBodyPart JavaDoc part = new MimeBodyPart JavaDoc();
320
321             StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
322             
323             Formatter f = getFormatter();
324             
325             String JavaDoc head = f.getHead(this);
326
327             if (head != null)
328             {
329                 sbuf.append(head);
330             }
331
332             int len = cb.length();
333
334             for (int i = 0; i < len; i++)
335             {
336                 LogRecord record = cb.get();
337                 sbuf.append(getFormatter().format(record));
338             }
339
340             String JavaDoc tail = getFormatter().getTail(this);
341
342             if (tail != null)
343             {
344                 sbuf.append(tail);
345             }
346
347             part.setContent(sbuf.toString(), getEmailContentType());
348
349             Multipart JavaDoc mp = new MimeMultipart JavaDoc();
350             mp.addBodyPart(part);
351             msg.setContent(mp);
352             
353             msg.setSentDate(new Date JavaDoc());
354             
355             Transport.send(msg);
356         }
357         catch (Exception JavaDoc ex)
358         {
359             this.reportError("sendBuffer",
360                         ex,
361                         DEFAULT_ERROR_CODE);
362         }
363     }
364
365     protected String JavaDoc getEmailContentType()
366     {
367         return "text/plain";
368     }
369     
370     /**
371        Returns value of the <b>EvaluatorClass</b> option.
372      */

373     public String JavaDoc getEvaluatorClass()
374     {
375         return (evaluator == null) ? null : evaluator.getClass().getName();
376     }
377
378     /**
379        Returns value of the <b>From</b> option.
380      */

381     public String JavaDoc getFrom()
382     {
383         return from;
384     }
385
386     /**
387        Returns value of the <b>Subject</b> option.
388      */

389     public String JavaDoc getSubject()
390     {
391         return subject;
392     }
393
394     /**
395        Returns value of the <b>SmtpHost</b> option.
396      */

397     public String JavaDoc getSmtpHost()
398     {
399         return smtpHost;
400     }
401
402     public void flush()
403     {
404         // todo - implement
405
}
406
407     /**
408        Returns value of the <b>bufferSize</b> option.
409      */

410     public int getBufferSize()
411     {
412         return bufferSize;
413     }
414
415     /**
416        The <b>from</b> option takes a string value which should be a
417        e-mail address of the sender.
418      */

419     public void setFrom(String JavaDoc from)
420     {
421         this.from = from;
422     }
423
424     /**
425        The <b>to</b> option takes a string value which should be a
426        e-mail address of the sender.
427      */

428     public void setTo(String JavaDoc to)
429     {
430         this.to = to;
431     }
432
433     /**
434        The <b>subject</b> option takes a string value which should be a
435        the subject of the e-mail message.
436      */

437     public void setSubject(String JavaDoc subject)
438     {
439         this.subject = subject;
440     }
441
442     /**
443        The <b>bufferSize</b> option takes a positive integer
444        representing the maximum number of logging records to collect in a
445        cyclic buffer. When the <code>bufferSize</code> is reached,
446        oldest records are deleted as new records are added to the
447        buffer. By default the size of the cyclic buffer is 512 records.
448      */

449     public void setBufferSize(int bufferSize)
450     {
451         this.bufferSize = bufferSize;
452         cb.resize(bufferSize);
453     }
454
455     /**
456        The <b>smtpHost</b> option takes a string value which should be a
457        the host name of the SMTP server that will send the e-mail message.
458      */

459     public void setSmtpHost(String JavaDoc smtpHost)
460     {
461         this.smtpHost = smtpHost;
462     }
463
464     /**
465        The <b>evaluatorClass</b> option takes a string value
466        representing the name of the class implementing the {@link
467        TriggeringRecordEvaluator} interface. A corresponding object will
468        be instantiated and assigned as the triggering record evaluator
469        for the SMTPHandler.
470      */

471     public void setEvaluatorClass(String JavaDoc value)
472     {
473         evaluator =
474         (TriggeringRecordEvaluator) instantiateByClassName(value, new DefaultEvaluator());
475     }
476
477     static Object JavaDoc instantiateByClassName(String JavaDoc strClassName, Object JavaDoc defaultObj)
478     {
479         Object JavaDoc result;
480         
481         try
482         {
483             ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
484             Class JavaDoc clazz = loader.loadClass(strClassName);
485             result = clazz.newInstance();
486         }
487         catch (Exception JavaDoc ex)
488         {
489             result = defaultObj;
490         }
491         return result;
492
493     }
494     public String JavaDoc toString()
495     {
496         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
497         sb.append("SmtpHost=");
498         sb.append(String.valueOf(this.getSmtpHost()));
499         sb.append("\nFrom=");
500         sb.append(String.valueOf(this.getFrom()));
501         sb.append("\nTo=");
502         sb.append(String.valueOf(this.getTo()));
503         sb.append("\nSubject=");
504         sb.append(String.valueOf(this.getSubject()));
505         sb.append("\nFormatter=");
506         sb.append(String.valueOf(this.getFormatter()));
507         sb.append("\nLevel=");
508         sb.append(String.valueOf(this.getLevel()));
509         sb.append("\nBufferSize=");
510         sb.append(String.valueOf(this.getBufferSize()));
511         return sb.toString();
512     }
513
514
515     public String JavaDoc getSmtpPassword()
516     {
517         return smtpPassword;
518     }
519
520
521     public void setSmtpPassword(String JavaDoc value)
522     {
523         this.smtpPassword = value;
524     }
525
526
527     public String JavaDoc getSmtpUsername()
528     {
529         return smtpUsername;
530     }
531
532
533     public void setSmtpUsername(String JavaDoc value)
534     {
535         this.smtpUsername = value;
536     }
537
538 }
539
540 class DefaultEvaluator implements TriggeringRecordEvaluator
541 {
542     /**
543     
544        Should this <code>record</code> trigger an email message to
545        be sent?
546     
547        <p>This method returns <code>true</code>, if the record level
548        has WARNING level or higher. Otherwise it returns
549        <code>false</code>.
550         
551        */

552     public boolean isTriggeringRecord(final LogRecord record)
553     {
554         boolean result = false;
555         
556         if (record == null)
557         {
558             result = false;
559         }
560         else if (record.getLevel() == null)
561         {
562             result = false;
563         }
564         else
565         {
566             result = record.getLevel().intValue() >= java.util.logging.Level.WARNING.intValue();
567         }
568         
569         return result;
570     }
571     
572
573 }
574
Popular Tags