package se.l4.vibe.mail;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import se.l4.vibe.backend.VibeBackend;
import se.l4.vibe.event.EventListener;
import se.l4.vibe.event.EventSeverity;
import se.l4.vibe.event.Events;
import se.l4.vibe.probes.Probe;
import se.l4.vibe.probes.Sampler;
import se.l4.vibe.timer.Timer;
/**
* Backend that will e-mail events.
*
* @author Andreas Holstenson
*
*/
public class MailBackend
implements VibeBackend
{
private final EventSeverity minimumSeverity;
private final ExecutorService executor;
private final String from;
private final String receivers[];
private final String subject;
private final AuthenticatorImpl authenticator;
private final String smtpServer;
private final int smtpPort;
private final boolean tls;
private final boolean ssl;
public MailBackend(
EventSeverity minimumSeverity,
String smtpServer,
int smtpPort,
String from,
String[] receivers,
String subject,
boolean tls,
boolean ssl,
AuthenticatorImpl authenticator)
{
this.minimumSeverity = minimumSeverity;
this.smtpServer = smtpServer;
this.smtpPort = smtpPort;
this.from = from;
this.receivers = receivers;
this.subject = subject;
this.tls = tls;
this.ssl = ssl;
this.authenticator = authenticator;
executor = Executors.newCachedThreadPool(new ThreadFactory()
{
@Override
public Thread newThread(Runnable r)
{
return new Thread(r, "vibe-mail");
}
});
}
/**
* Stop the mail backend.
*/
public void stop()
{
executor.shutdown();
}
private void send(String path, long time, EventSeverity severity, Object event)
throws MessagingException
{
Properties props = System.getProperties();
props.put("mail.smtp.host", smtpServer);
props.put("mail.smtp.port", smtpPort);
if(tls)
{
props.put("mail.smtp.starttls.enable", "true");
}
else if(ssl)
{
props.put("mail.smtp.socketFactory.port", smtpPort);
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
if(authenticator != null)
{
props.setProperty("mail.smtp.submitter", authenticator
.getPasswordAuthentication()
.getUserName());
props.setProperty("mail.smtp.auth", "true");
}
Session session = Session.getDefaultInstance(props, authenticator);
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
InternetAddress[] receivers = new InternetAddress[this.receivers.length];
for(int i=0, n=receivers.length; i<n; i++)
{
receivers[i] = new InternetAddress(this.receivers[i]);
}
msg.setRecipients(Message.RecipientType.TO, receivers);
msg.setSubject(subject
.replace("{severity}", severity.toString())
.replace("{path}", path)
);
StringBuilder body = new StringBuilder();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
body
.append(path)
.append(": The following ")
.append(severity)
.append(" event was received at ")
.append(sdf.format(new Date(time)))
.append(":\n\n")
.append(event);
msg.setText(body.toString());
msg.setHeader("X-Mailer", "Vibe");
msg.setHeader("X-Vibe-Severity", severity.toString());
msg.setHeader("X-Vibe-Path", path);
msg.setSentDate(new Date());
Transport.send(msg);
}
@Override
public void export(String path, Sampler<?> series)
{
// Do nothing, not supported by mailer
}
@Override
public void export(String path, Probe<?> probe)
{
// Do nothing, not supported by mailer
}
@Override
public void export(String path, Timer timer)
{
// Do nothing, not supported by mailer
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void export(final String path, Events<?> events)
{
events.addListener(new EventListener()
{
@Override
public void eventRegistered(Events events,
final EventSeverity severity,
final Object event)
{
if(severity.ordinal() < minimumSeverity.ordinal())
{
return;
}
final long time = System.currentTimeMillis();
executor.submit(new Runnable()
{
@Override
public void run()
{
try
{
send(path, time, severity, event);
}
catch(MessagingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
});
}
@Override
public void close()
{
executor.shutdown();
}
/**
* Start building a new mail backend.
*
* @return
*/
public static Builder builder()
{
return new Builder();
}
private static class AuthenticatorImpl
extends javax.mail.Authenticator
{
private PasswordAuthentication authentication;
public AuthenticatorImpl(String username, String password)
{
authentication = new PasswordAuthentication(username, password);
}
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return authentication;
}
}
public static class Builder
{
private EventSeverity minimumSeverity;
private String subject;
private String from;
private List<String> receivers;
private String smtpServer;
private int smtpPort;
private boolean smtpTls;
private boolean smtpSsl;
private AuthenticatorImpl authenticator;
public Builder()
{
receivers = new ArrayList<String>();
minimumSeverity = EventSeverity.ERROR;
subject = "{path}: Event with {severity} severity received";
smtpPort = 25;
}
/**
* Set the subject of sent e-mails. Use {@literal {severity}} to replace
* part of the subject with the severity of the event.
*
* @param subject
* @return
*/
public Builder setSubject(String subject)
{
this.subject = subject;
return this;
}
/**
* Set the minimum severity for events that should be mailed.
*
* @param minimumSeverity
*/
public Builder setMinimumSeverity(EventSeverity minimumSeverity)
{
this.minimumSeverity = minimumSeverity;
return this;
}
/**
* Set which e-mail to use as the sender.
*
* @param from
* @return
*/
public Builder setSender(String from)
{
this.from = from;
return this;
}
/**
* Add a recipient to the backend.
*
* @param email
* @return
*/
public Builder addRecipient(String email)
{
receivers.add(email);
return this;
}
/**
* Set which SMTP server to use.
*
* @param server
* @return
*/
public Builder setSmtpServer(String server)
{
this.smtpServer = server;
return this;
}
/**
* Set which port the SMTP server uses.
*
* @param port
* @return
*/
public Builder setSmtpPort(int port)
{
this.smtpPort = port;
return this;
}
/**
* Indicate that TLS should be used with the SMTP server.
*
* @param tls
* @return
*/
public Builder setSmtpTls(boolean tls)
{
this.smtpTls = tls;
return this;
}
/**
* Indicate that SSL should be used with the SMTP server.
*
* @param ssl
* @return
*/
public Builder setSmtpSsl(boolean ssl)
{
this.smtpSsl = ssl;
return this;
}
/**
* Set authentication for the SMTP server.
*
* @param username
* @param password
* @return
*/
public Builder setAuthentication(String username, String password)
{
authenticator = new AuthenticatorImpl(username, password);
return this;
}
/**
* Create the backend.
*
* @return
*/
public MailBackend build()
{
if(minimumSeverity == null)
{
throw new IllegalArgumentException("Minimum severity can not be null");
}
if(smtpServer == null)
{
throw new IllegalArgumentException("SMTP server needs to be specified");
}
if(receivers.isEmpty())
{
throw new IllegalArgumentException("At least one recipient needs to be specified");
}
if(from == null)
{
throw new IllegalArgumentException("A sender needs to be specified");
}
return new MailBackend(
minimumSeverity,
smtpServer,
smtpPort,
from,
receivers.toArray(new String[receivers.size()]),
subject,
smtpTls,
smtpSsl,
authenticator
);
}
}
}