Article:
  Reporting Application Errors by Email
Subject:   My own SMTPAppender class stopped to send errors
Date:   2007-09-13 01:35:21
From:   ReinhardK
Hi,


I have following problem: I have written my own SMTPAppender class to collect errors coming during one minute and to send it all together in one E-Mail. It seems that it works, but after a while JBoss stop to send error e-mails and it seems that he is hanging in a thread. JBoss stops always at different time, sometimes after 6 hours, sometimes after 30 minutes. At the bottom the code of my class. Please help me.


import java.util.Date;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;


import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.TriggeringEventEvaluator;


public class SMTPAppender extends AppenderSkeleton{


private static final Log log = LogFactory.getLog(SMTPAppender.class);
private String to;
private String cc;
private String bcc;
private String from;
private String subject;
private String smtpHost;
private String smtpUsername;
private String smtpPassword;
private boolean smtpDebug = false;
private boolean locationInfo = false;
protected Message msg;
protected TriggeringEventEvaluator evaluator;
private int amountOfSendingErrors = 50;
boolean firstMessage = false;
private Date firstIncomingTime = null;
private Timer timer = new Timer();
private TimerTask timerTask = null;
private LinkedList<LoggingEvent> loggingEventList = new LinkedList<LoggingEvent>();
int amountOfIncomingMessages = 0;
private int timeBufferInMili = 60000;

public SMTPAppender(){
this(new DefaultEvaluator());
}

public SMTPAppender(TriggeringEventEvaluator evaluator){
this.evaluator = evaluator;
}

public void activateOptions(){
log.debug("activateOptions()");

Session session = createSession();
msg = new MimeMessage(session);

try{
addressMessage(msg);
if (subject != null){
msg.setSubject(subject);
}
}
catch (MessagingException e){
log.error("Could not activate SMTPAppender options.", e);
}

log.debug("activateOptions() finished");
}

protected void addressMessage(final Message msg) throws MessagingException{
log.debug("addressMessage()");

if (from != null){
msg.setFrom(getAddress(from));
}
else{
msg.setFrom();
}

if (to != null && to.length() > 0){
msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
}

if (cc != null && cc.length() > 0){
msg.setRecipients(Message.RecipientType.CC, parseAddress(cc));
}

if (bcc != null && bcc.length() > 0){
msg.setRecipients(Message.RecipientType.BCC, parseAddress(bcc));
}

log.debug("addressMessage() finished");
}

protected Session createSession(){
log.debug("createSession()");

Properties props = null;
try{
props = new Properties(System.getProperties());
}
catch (SecurityException ex){
props = new Properties();
}
if (smtpHost != null){
props.put("mail.smtp.host", smtpHost);
}

Authenticator auth = null;
if (smtpPassword != null && smtpUsername != null){
props.put("mail.smtp.auth", "true");
auth = new Authenticator(){
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
};
}
Session session = Session.getInstance(props, auth);
if (smtpDebug){
session.setDebug(smtpDebug);
}

log.debug("createSession() finished!");
return session;
}

public synchronized void append(LoggingEvent event){
if (!checkEntryConditions()){
return;
}

event.getThreadName();
event.getNDC();
event.getMDCCopy();
if (locationInfo){
event.getLocationInformation();
}

if (evaluator.isTriggeringEvent(event)){
this.loggingEventList.addLast(event);

if (!this.firstMessage){
this.firstIncomingTime = new Date();
this.firstMessage = true;

timerTask = new TimerTask(){
public void run(){
SMTPAppender.this.sendBuffer();
SMTPAppender.this.firstMessage = false;

}
};

this.timer.schedule(this.timerTask, this.timeBufferInMili + 10000);

if (log.isDebugEnabled())
log.debug("Timer task created for " + this.timeBufferInMili + 10000);
}

if ((this.firstIncomingTime.getTime() + this.timeBufferInMili) < new Date().getTime()){
this.timerTask.cancel();
sendBuffer();
this.firstMessage = false;
}
}

log.debug("append() finished");
}

protected boolean checkEntryConditions(){
if (this.msg == null){
errorHandler.error("Message object not configured.");
return false;
}

if (this.evaluator == null){
errorHandler.error("No TriggeringEventEvaluator is set for appender [" + name + "].");
return false;
}

if (this.layout == null){
errorHandler.error("No layout set for appender named [" + name + "].");
return false;
}
return true;
}

synchronized public void close()
{
this.closed = true;
}

InternetAddress getAddress(String addressStr){
try{
return new InternetAddress(addressStr);
}
catch (AddressException e){
errorHandler.error("Could not parse address [" + addressStr + "].", e, ErrorCode.ADDRESS_PARSE_FAILURE);
return null;
}
}

InternetAddress[] parseAddress(String addressStr){
try{
return InternetAddress.parse(addressStr, true);
}
catch (AddressException e){
errorHandler.error("Could not parse address [" + addressStr + "].", e, ErrorCode.ADDRESS_PARSE_FAILURE);
return null;
}
}

public String getTo(){
return to;
}

public boolean requiresLayout(){
return true;
}

protected synchronized void sendBuffer(){
this.amountOfIncomingMessages = this.loggingEventList.size();

try{
MimeBodyPart part = new MimeBodyPart();

StringBuffer sbuf = new StringBuffer();
String t = layout.getHeader();
if (t != null)
sbuf.append(t);
int len = this.loggingEventList.size();
if (len > this.amountOfSendingErrors)
len = this.amountOfSendingErrors;

for (int i = 0; i < len; i++){
LoggingEvent event = this.loggingEventList.poll();

if (this.amountOfIncomingMessages > this.amountOfSendingErrors)
sbuf.append(MimeUtility.encodeText(cutMessage(layout.format(event))) + "\n");
else
sbuf.append(MimeUtility.encodeText(layout.format(event)) + "\n");

if (layout.ignoresThrowable()){
String[] s = event.getThrowableStrRep();

if (s != null){
for (int j = 0; j < s.length; j++){
sbuf.append(s[j]);
sbuf.append(Layout.LINE_SEP);
}
}
}
}
t = layout.getFooter();
if (t != null)
sbuf.append(t);
if (len == this.amountOfSendingErrors)
part.setContent(createAdvancedContent(sbuf.toString()), layout.getContentType());
else
part.setContent(sbuf.toString(), layout.getContentType());

Multipart mp = new MimeMultipart();
mp.addBodyPart(part);
msg.setContent(mp);

msg.setSentDate(new Date());
Transport.send(msg);
this.amountOfIncomingMessages = 0;
this.loggingEventList.clear();
}
catch (Exception e){
log.error(sendBuffer runs into Exception: ", e);
}
}

private String createAdvancedContent(String content){
return "Within the last " + (this.timeBufferInMili / 1000) + " seconds " + this.amountOfIncomingMessages + " errors occured. Only the first "
+ this.amountOfSendingErrors + " errors will be forwarded in this email! \n\n" + content;
}

private String cutMessage(String message){
String messageBuffer = "";
try{
int firstIndexOfBreak = message.indexOf("\n");
if (firstIndexOfBreak > 0){
messageBuffer = message.substring(0, firstIndexOfBreak);

String substring = "";

if ((firstIndexOfBreak + 2) < message.length())
substring = message.substring(firstIndexOfBreak + 2, message.length());

int secondIndexOfBreak = substring.indexOf("\n");

if (secondIndexOfBreak > 0)

if (firstIndexOfBreak < (firstIndexOfBreak + 2 + secondIndexOfBreak))
messageBuffer += message.substring(firstIndexOfBreak, firstIndexOfBreak + 2 + secondIndexOfBreak);
}
}
catch (Exception e){
log.error("cutMessage runs into Exception: ", e);
}

return messageBuffer + "\n";
}

public String getEvaluatorClass(){
return evaluator == null ? null : evaluator.getClass().getName();
}
public String getFrom(){
return from;
}
public String getSubject(){
return subject;
}
public void setFrom(String from){
this.from = from;
}
public void setSubject(String subject){
this.subject = subject;
}
public void setSMTPHost(String smtpHost){
this.smtpHost = smtpHost;
}
public String getSMTPHost(){
return smtpHost;
}
public void setTo(String to){
this.to = to;
}
public void setEvaluatorClass(String value){
evaluator = (TriggeringEventEvaluator) OptionConverter.instantiateByClassName(value, TriggeringEventEvaluator.class, evaluator);
}
public void setLocationInfo(boolean locationInfo){
this.locationInfo = locationInfo;
}
public boolean getLocationInfo(){
return locationInfo;
}
public void setCc(final String addresses){
this.cc = addresses;
}
public String getCc(){
return cc;
}
public void setBcc(final String addresses){
this.bcc = addresses;
}
public String getBcc(){
return bcc;
}
public void setSMTPPassword(final String password){
this.smtpPassword = password;
}
public void setSMTPUsername(final String username){
this.smtpUsername = username;
}
public void setSMTPDebug(final boolean debug){
this.smtpDebug = debug;
}
public String getSMTPPassword(){
return smtpPassword;
}
public String getSMTPUsername(){
return smtpUsername;
}
public boolean getSMTPDebug(){
return smtpDebug;
}
public int getAmountOfSendingErrors(){
return amountOfSendingErrors;
}
public void setAmountOfSendingErrors(int amountOfSendingErrors){
this.amountOfSendingErrors = amountOfSendingErrors;
}
public int getTimeBufferInMili(){
return timeBufferInMili;
}
public void setTimeBufferInMili(int timeBufferInMili){
this.timeBufferInMili = timeBufferInMili;
}
}


class DefaultEvaluator implements TriggeringEventEvaluator{
public boolean isTriggeringEvent(LoggingEvent event){
return event.getLevel().isGreaterOrEqual(Level.ERROR);
}
}