package com.urbanairship.octobot; import org.apache.log4j.Logger; import java.util.concurrent.ArrayBlockingQueue; // E-mail Imports import javax.mail.Message; import javax.mail.Session; import javax.mail.Transport; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Properties; // This singleton class provides an internal queue allowing us to asynchronously // send email notifications rather than processing them in main app loop. public class MailQueue implements Runnable { private static final Logger logger = Logger.getLogger("Email Queue"); private static String from = Settings.get("Octobot", "email_from"); private static String recipient = Settings.get("Octobot", "email_to"); private static String server = Settings.get("Octobot", "email_server"); private static String username = Settings.get("Octobot", "email_username"); private static String password = Settings.get("Octobot", "email_password"); private static Integer port = Settings.getAsInt("Octobot", "email_port"); private static Boolean useSSL = Settings.getAsBoolean("Octobot", "email_ssl"); private static Boolean useAuth = Settings.getAsBoolean("Octobot", "email_auth"); // This internal queue is backed by an ArrayBlockingQueue. By specifying the // number of messages to be held here before the queue blocks (below), we // provide ourselves a safety threshold in terms of how many messages could // be backed up before we force the delivery of all current waiting messages. private static ArrayBlockingQueue<String> messages; // Initialize the queue's singleton instance. private static final MailQueue INSTANCE = new MailQueue(); private MailQueue() { messages = new ArrayBlockingQueue<String>(100); } public static MailQueue get() { return INSTANCE; } public static void put(String message) throws InterruptedException { messages.put(message); } public static int size() { return messages.size(); } public static int remainingCapacity() { return messages.remainingCapacity(); } // As this thread runs, it consumes messages from the internal queue and // delivers each to the recipients configured in the YML file. public void run() { if (!validSettings()) { logger.error("Email settings invalid; check your configuration."); return; } while (true) { try { String message = messages.take(); deliverMessage(message); } catch (InterruptedException e) { // Pass } } } // Delivers email error notificiations. private void deliverMessage(String message) { logger.info("Sending error notification to: " + recipient); try { MimeMessage email = prepareEmail(); email.setFrom(new InternetAddress(from)); email.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient)); email.setSubject("Task Error Notification"); email.setText(message); // Send message Transport.send(email); logger.info("Sent error e-mail to " + recipient + ". " + "Message: \n\n" + message); } catch (MessagingException e) { logger.error("Error delivering error notification.", e); } } // Prepares a sendable email object based on Octobot's SMTP, SSL, and // Authentication configuration. private MimeMessage prepareEmail() { // Prepare our configuration. Properties properties = System.getProperties(); properties.setProperty("mail.smtp.host", server); properties.put("mail.smtp.auth", "true"); Session session = null; // Configure SSL. if (useSSL) { properties.put("mail.smtp.socketFactory.port", port); properties.put("mail.smtp.starttls.enable","true"); properties.put("mail.smtp.socketFactory.fallback", "false"); properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); } // Configure authentication. if (useAuth) { properties.setProperty("mail.smtp.submitter", username); Authenticator authenticator = new Authenticator(username, password); session = Session.getInstance(properties, authenticator); } else { session = Session.getDefaultInstance(properties); } return new MimeMessage(session); } // Provides an SMTP authenticator for messages sent with auth. private class Authenticator extends javax.mail.Authenticator { private PasswordAuthentication authentication; public Authenticator(String user, String pass) { String username = user; String password = pass; authentication = new PasswordAuthentication(username, password); } protected PasswordAuthentication getPasswordAuthentication() { return authentication; } } private boolean validSettings() { boolean result = true; Object[] settings = new Object[]{from, recipient, server, port}; // Validate base settings. for (Object setting : settings) if (setting == null) result = false; // Validate authentication. if (useAuth && (username == null || password == null)) result = false; return result; } }