KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log4j > net > SMTPAppender


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.log4j.net;
18
19 import org.apache.log4j.AppenderSkeleton;
20 import org.apache.log4j.Level;
21 import org.apache.log4j.helpers.CyclicBuffer;
22 import org.apache.log4j.helpers.OptionConverter;
23 import org.apache.log4j.helpers.LogLog;
24 import org.apache.log4j.spi.LoggingEvent;
25 import org.apache.log4j.spi.ErrorCode;
26 import org.apache.log4j.spi.TriggeringEventEvaluator;
27 import java.util.Properties JavaDoc;
28 import java.util.Date JavaDoc;
29
30 import javax.mail.Session JavaDoc;
31 import javax.mail.Transport JavaDoc;
32 import javax.mail.Message JavaDoc;
33 import javax.mail.MessagingException JavaDoc;
34 import javax.mail.internet.MimeMessage JavaDoc;
35 import javax.mail.Multipart JavaDoc;
36 import javax.mail.internet.MimeMultipart JavaDoc;
37 import javax.mail.internet.MimeBodyPart JavaDoc;
38 import javax.mail.internet.InternetAddress JavaDoc;
39 import javax.mail.internet.AddressException JavaDoc;
40
41 /**
42    Send an e-mail when a specific logging event occurs, typically on
43    errors or fatal errors.
44
45    <p>The number of logging events delivered in this e-mail depend on
46    the value of <b>BufferSize</b> option. The
47    <code>SMTPAppender</code> keeps only the last
48    <code>BufferSize</code> logging events in its cyclic buffer. This
49    keeps memory requirements at a reasonable level while still
50    delivering useful application context.
51
52    @author Ceki G&uuml;lc&uuml;
53    @since 1.0 */

54 public class SMTPAppender extends AppenderSkeleton {
55   private String JavaDoc to;
56   private String JavaDoc from;
57   private String JavaDoc subject;
58   private String JavaDoc smtpHost;
59   private int bufferSize = 512;
60   private boolean locationInfo = false;
61
62   protected CyclicBuffer cb = new CyclicBuffer(bufferSize);
63   protected Message JavaDoc msg;
64
65   protected TriggeringEventEvaluator evaluator;
66
67
68
69   /**
70      The default constructor will instantiate the appender with a
71      {@link TriggeringEventEvaluator} that will trigger on events with
72      level ERROR or higher.*/

73   public
74   SMTPAppender() {
75     this(new DefaultEvaluator());
76   }
77
78
79   /**
80      Use <code>evaluator</code> passed as parameter as the {@link
81      TriggeringEventEvaluator} for this SMTPAppender. */

82   public
83   SMTPAppender(TriggeringEventEvaluator evaluator) {
84     this.evaluator = evaluator;
85   }
86
87
88   /**
89      Activate the specified options, such as the smtp host, the
90      recipient, from, etc. */

91   public
92   void activateOptions() {
93     Properties JavaDoc props = new Properties JavaDoc (System.getProperties());
94     if (smtpHost != null)
95       props.put("mail.smtp.host", smtpHost);
96
97
98     Session JavaDoc session = Session.getInstance(props, null);
99     //session.setDebug(true);
100
msg = new MimeMessage JavaDoc(session);
101
102      try {
103        if (from != null)
104      msg.setFrom(getAddress(from));
105        else
106      msg.setFrom();
107
108        msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
109        if(subject != null)
110      msg.setSubject(subject);
111      } catch(MessagingException JavaDoc e) {
112        LogLog.error("Could not activate SMTPAppender options.", e );
113      }
114   }
115
116   /**
117      Perform SMTPAppender specific appending actions, mainly adding
118      the event to a cyclic buffer and checking if the event triggers
119      an e-mail to be sent. */

120   public
121   void append(LoggingEvent event) {
122
123     if(!checkEntryConditions()) {
124       return;
125     }
126
127     event.getThreadName();
128     event.getNDC();
129     if(locationInfo) {
130       event.getLocationInformation();
131     }
132     cb.add(event);
133     if(evaluator.isTriggeringEvent(event)) {
134       sendBuffer();
135     }
136   }
137
138  /**
139      This method determines if there is a sense in attempting to append.
140
141      <p>It checks whether there is a set output target and also if
142      there is a set layout. If these checks fail, then the boolean
143      value <code>false</code> is returned. */

144   protected
145   boolean checkEntryConditions() {
146     if(this.msg == null) {
147       errorHandler.error("Message object not configured.");
148       return false;
149     }
150
151     if(this.evaluator == null) {
152       errorHandler.error("No TriggeringEventEvaluator is set for appender ["+
153              name+"].");
154       return false;
155     }
156
157
158     if(this.layout == null) {
159       errorHandler.error("No layout set for appender named ["+name+"].");
160       return false;
161     }
162     return true;
163   }
164
165
166   synchronized
167   public
168   void close() {
169     this.closed = true;
170   }
171
172   InternetAddress JavaDoc getAddress(String JavaDoc addressStr) {
173     try {
174       return new InternetAddress JavaDoc(addressStr);
175     } catch(AddressException JavaDoc e) {
176       errorHandler.error("Could not parse address ["+addressStr+"].", e,
177              ErrorCode.ADDRESS_PARSE_FAILURE);
178       return null;
179     }
180   }
181
182   InternetAddress JavaDoc[] parseAddress(String JavaDoc addressStr) {
183     try {
184       return InternetAddress.parse(addressStr, true);
185     } catch(AddressException JavaDoc e) {
186       errorHandler.error("Could not parse address ["+addressStr+"].", e,
187              ErrorCode.ADDRESS_PARSE_FAILURE);
188       return null;
189     }
190   }
191
192   /**
193      Returns value of the <b>To</b> option.
194    */

195   public
196   String JavaDoc getTo() {
197     return to;
198   }
199
200
201   /**
202      The <code>SMTPAppender</code> requires a {@link
203      org.apache.log4j.Layout layout}. */

204   public
205   boolean requiresLayout() {
206     return true;
207   }
208
209   /**
210      Send the contents of the cyclic buffer as an e-mail message.
211    */

212   protected
213   void sendBuffer() {
214
215     // Note: this code already owns the monitor for this
216
// appender. This frees us from needing to synchronize on 'cb'.
217
try {
218       MimeBodyPart JavaDoc part = new MimeBodyPart JavaDoc();
219
220       StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
221       String JavaDoc t = layout.getHeader();
222       if(t != null)
223     sbuf.append(t);
224       int len = cb.length();
225       for(int i = 0; i < len; i++) {
226     //sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
227
LoggingEvent event = cb.get();
228     sbuf.append(layout.format(event));
229     if(layout.ignoresThrowable()) {
230       String JavaDoc[] s = event.getThrowableStrRep();
231       if (s != null) {
232         for(int j = 0; j < s.length; j++) {
233           sbuf.append(s[j]);
234         }
235       }
236     }
237       }
238       t = layout.getFooter();
239       if(t != null)
240     sbuf.append(t);
241       part.setContent(sbuf.toString(), layout.getContentType());
242
243       Multipart JavaDoc mp = new MimeMultipart JavaDoc();
244       mp.addBodyPart(part);
245       msg.setContent(mp);
246
247       msg.setSentDate(new Date JavaDoc());
248       Transport.send(msg);
249     } catch(Exception JavaDoc e) {
250       LogLog.error("Error occured while sending e-mail notification.", e);
251     }
252   }
253
254
255
256   /**
257      Returns value of the <b>EvaluatorClass</b> option.
258    */

259   public
260   String JavaDoc getEvaluatorClass() {
261     return evaluator == null ? null : evaluator.getClass().getName();
262   }
263
264   /**
265      Returns value of the <b>From</b> option.
266    */

267   public
268   String JavaDoc getFrom() {
269     return from;
270   }
271
272   /**
273      Returns value of the <b>Subject</b> option.
274    */

275   public
276   String JavaDoc getSubject() {
277     return subject;
278   }
279
280   /**
281      The <b>From</b> option takes a string value which should be a
282      e-mail address of the sender.
283    */

284   public
285   void setFrom(String JavaDoc from) {
286     this.from = from;
287   }
288
289   /**
290      The <b>Subject</b> option takes a string value which should be a
291      the subject of the e-mail message.
292    */

293   public
294   void setSubject(String JavaDoc subject) {
295     this.subject = subject;
296   }
297
298
299   /**
300      The <b>BufferSize</b> option takes a positive integer
301      representing the maximum number of logging events to collect in a
302      cyclic buffer. When the <code>BufferSize</code> is reached,
303      oldest events are deleted as new events are added to the
304      buffer. By default the size of the cyclic buffer is 512 events.
305    */

306   public
307   void setBufferSize(int bufferSize) {
308     this.bufferSize = bufferSize;
309     cb.resize(bufferSize);
310   }
311
312   /**
313      The <b>SMTPHost</b> option takes a string value which should be a
314      the host name of the SMTP server that will send the e-mail message.
315    */

316   public
317   void setSMTPHost(String JavaDoc smtpHost) {
318     this.smtpHost = smtpHost;
319   }
320
321   /**
322      Returns value of the <b>SMTPHost</b> option.
323    */

324   public
325   String JavaDoc getSMTPHost() {
326     return smtpHost;
327   }
328
329   /**
330      The <b>To</b> option takes a string value which should be a
331      comma separated list of e-mail address of the recipients.
332    */

333   public
334   void setTo(String JavaDoc to) {
335     this.to = to;
336   }
337
338
339
340   /**
341      Returns value of the <b>BufferSize</b> option.
342    */

343   public
344   int getBufferSize() {
345     return bufferSize;
346   }
347
348   /**
349      The <b>EvaluatorClass</b> option takes a string value
350      representing the name of the class implementing the {@link
351      TriggeringEventEvaluator} interface. A corresponding object will
352      be instantiated and assigned as the triggering event evaluator
353      for the SMTPAppender.
354    */

355   public
356   void setEvaluatorClass(String JavaDoc value) {
357       evaluator = (TriggeringEventEvaluator)
358                 OptionConverter.instantiateByClassName(value,
359                        TriggeringEventEvaluator.class,
360                                evaluator);
361   }
362
363
364   /**
365      The <b>LocationInfo</b> option takes a boolean value. By
366      default, it is set to false which means there will be no effort
367      to extract the location information related to the event. As a
368      result, the layout that formats the events as they are sent out
369      in an e-mail is likely to place the wrong location information
370      (if present in the format).
371
372      <p>Location information extraction is comparatively very slow and
373      should be avoided unless performance is not a concern.
374    */

375   public
376   void setLocationInfo(boolean locationInfo) {
377     this.locationInfo = locationInfo;
378   }
379
380   /**
381      Returns value of the <b>LocationInfo</b> option.
382    */

383   public
384   boolean getLocationInfo() {
385     return locationInfo;
386   }
387 }
388
389 class DefaultEvaluator implements TriggeringEventEvaluator {
390   /**
391      Is this <code>event</code> the e-mail triggering event?
392
393      <p>This method returns <code>true</code>, if the event level
394      has ERROR level or higher. Otherwise it returns
395      <code>false</code>. */

396   public
397   boolean isTriggeringEvent(LoggingEvent event) {
398     return event.getLevel().isGreaterOrEqual(Level.ERROR);
399   }
400 }
401
Popular Tags