org.snopoke.util.Emailer.java Source code

Java tutorial

Introduction

Here is the source code for org.snopoke.util.Emailer.java

Source

/*
 * Emailer.java - utility class for sending email messages
 * Copyright (C) 2012  Simon Kelly
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.snopoke.util;

import static org.apache.commons.lang.Validate.isTrue;
import static org.apache.commons.lang.Validate.notEmpty;
import static org.apache.commons.lang.Validate.notNull;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.activation.URLDataSource;
import javax.mail.MessagingException;
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.MimeMessage.RecipientType;
import javax.mail.internet.MimeMultipart;

/**
 * Utility class for sending email messages.
 * 
 * <pre>
 * {
 *    &#064;code
 *    Emailer m = new Emailer(host, port, username, password);
 *    m.setTLS(true);
 *    m.setDebug(true);
 *    m.setFrom(&quot;info@domain.com&quot;, &quot;Me Myself&quot;);
 *    m.addTo(&quot;anyone@test.com&quot;, &quot;Any Body&quot;);
 *    m.setSubject(&quot;Subject&quot;);
 * 
 *    String id = UUID.randomUUID().toString();
 *    m.addAttachment(AttachmentType.RESOURCE, &quot;classpath-image.gif&quot;, id);
 *    m.setHTMLContent(&quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;HTML&lt;/h1&gt;\n&lt;img src=\&quot;cid:&quot; + id + &quot;\&quot;/&gt;\n&lt;/body&gt;&lt;/html&gt;&quot;);
 * 
 *    m.setTextContent(&quot;Text version of HTML.&quot;);
 * 
 *    m.addAttachment(AttachmentType.FILE, &quot;/Users/me/file.txt&quot;);
 *    m.send();
 * }
 * </pre>
 * 
 * @author Simon Kelly <simongdkelly@gmail.com>
 */
public class Emailer {

    private final String host;
    private final int port;
    private final String username;
    private final String password;
    private Address from;
    private List<Address> to = new ArrayList<Emailer.Address>();
    private List<Address> cc = new ArrayList<Emailer.Address>();
    private List<Address> bcc = new ArrayList<Emailer.Address>();
    private List<Attachment> attachments = new ArrayList<Emailer.Attachment>();
    private String subject;
    private String textContent;
    private String htmlContent;
    private boolean useSSL = false;
    private boolean useTLS = false;
    private boolean debug = false;

    public Emailer(String host, int port, String username, String password) {
        notEmpty(host, "Host can not be empty");
        isTrue(port > 0, "Invalid port number");
        if (username != null || password != null) {
            notEmpty(username, "Both username and password must be supplied or neither");
            notEmpty(password, "Both username and password must be supplied or neither");
        }
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    /**
     * @param email
     * @param name
     *            can be null
     */
    public void setFrom(String email, String name) {
        from = new Address(email, name);
    }

    /**
     * @param email
     * @param name
     *            can be null
     */
    public void addTo(String email, String name) {
        to.add(new Address(email, name));
    }

    /**
     * @param email
     * @param name
     *            can be null
     */
    public void addCC(String email, String name) {
        cc.add(new Address(email, name));
    }

    /**
     * @param email
     * @param name
     *            can be null
     */
    public void addBCC(String email, String name) {
        bcc.add(new Address(email, name));
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public void setTextContent(String textContent) {
        this.textContent = textContent;
    }

    public void setHTMLContent(String htmlContent) {
        this.htmlContent = htmlContent;
    }

    public void addAttachment(AttachmentType type, String path) {
        addAttachment(type, path, UUID.randomUUID().toString());
    }

    /**
     * <p>
     * Add an attachment with a specific ID. This can be used to link
     * attachments in the content. e.g. images in HTML content
     * </p>
     * 
     * {@code <img src="cid:the-attachment-id"/> }
     * 
     * @param path
     * @param id
     */
    public void addAttachment(AttachmentType type, String path, String id) {
        this.attachments.add(new Attachment(type, path, id));
    }

    /**
     * Configure the session to use SSL.
     * 
     * @param useSSL
     */
    public void setSSL(boolean useSSL) {
        this.useSSL = useSSL;
    }

    /**
     * Configure the session to use TLS.
     * 
     * @param useTLS
     */
    public void setTLS(boolean useTLS) {
        this.useTLS = useTLS;
    }

    /**
     * Configure the mail session to print debug info. Also trust all mail
     * certifications.
     * 
     * Should not be used in production for security reasons.
     * 
     * @param debug
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    public void send() throws MessagingException {
        notNull(from, "From address can not be null");
        isTrue(!to.isEmpty() || !bcc.isEmpty(), "No TO or BCC addresses specified");

        MimeMessage message = getMessasge();

        // Cover wrap
        MimeBodyPart wrap = new MimeBodyPart();
        MimeMultipart cover = getCoverPart();
        wrap.setContent(cover);

        MimeMultipart content = new MimeMultipart("related");
        message.setContent(content);
        content.addBodyPart(wrap);

        addAttachments(content);

        Transport.send(message);
    }

    private void addAttachments(MimeMultipart content) throws MessagingException {
        for (Attachment att : attachments) {
            MimeBodyPart bodyPart = att.getBodyPart();
            if (bodyPart != null)
                content.addBodyPart(bodyPart);
        }
    }

    private MimeMultipart getCoverPart() throws MessagingException {
        // Alternative TEXT/HTML content
        MimeMultipart cover = new MimeMultipart("alternative");
        if (textContent != null && !textContent.isEmpty()) {
            MimeBodyPart text = new MimeBodyPart();
            cover.addBodyPart(text);
            text.setText(textContent);
        }

        if (htmlContent != null && !htmlContent.isEmpty()) {
            MimeBodyPart html = new MimeBodyPart();
            cover.addBodyPart(html);
            html.setContent(htmlContent, "text/html");
        }
        return cover;
    }

    /**
     * Create the MimeMessage and set the to/from/subject
     * 
     * @return MimeMessaeg
     * @throws MessagingException
     */
    private MimeMessage getMessasge() throws MessagingException {
        MimeMessage message = new MimeMessage(getSession());
        message.setSentDate(new Date());

        for (Address toAddress : to) {
            message.addRecipient(RecipientType.TO, toAddress.getInternetAddress());
        }

        for (Address toAddress : cc) {
            message.addRecipient(RecipientType.CC, toAddress.getInternetAddress());
        }

        for (Address toAddress : bcc) {
            message.addRecipient(RecipientType.BCC, toAddress.getInternetAddress());
        }

        message.setFrom(from.getInternetAddress());

        if (subject != null && !subject.isEmpty())
            message.setSubject(subject, "UTF-8");
        return message;
    }

    /**
     * Create the mail session with authentication and properties for SSL/TLS
     * etc.
     * 
     * @return configured mail session
     */
    private Session getSession() {

        Properties properties = new Properties();

        Authenticator authenticator = new Authenticator();
        if (username != null && password != null) {
            properties.put("mail.smtp.submitter", authenticator.getPasswordAuthentication().getUserName());
            properties.put("mail.smtp.auth", "true");
        }

        properties.put("mail.smtp.host", host);
        properties.put("mail.smtp.port", port);
        properties.put("mail.smtp.socketFactory.port", port);
        properties.put("mail.smtp.socketFactory.fallback", "false");
        if (useSSL)
            properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        if (useTLS)
            properties.put("mail.smtp.starttls.enable", "true");

        if (debug) {
            properties.put("mail.smtp.ssl.checkserveridentity", "false");
            properties.put("mail.smtp.ssl.trust", "*");
            properties.put("mail.debug", "true");
        }

        return Session.getInstance(properties, authenticator);
    }

    @Override
    public String toString() {
        return "Email [host=" + host + "\nport=" + port + "\nusername=" + username + "\npassword=" + password
                + "\nfrom=" + from + "\nto=" + to + "\nattachments=" + attachments + "\nsubject=" + subject
                + "\ntextContent=" + textContent + "\nhtmlContent=" + htmlContent + "\nuseSSL=" + useSSL
                + "\nuseTLS=" + useTLS + "\ndebug=" + debug + "]";
    }

    /**
     * Used to indicate how to load the attachment.
     */
    public enum AttachmentType {
        /**
         * Attachment is loaded from the file system. Path must be relative or
         * absolute
         */
        FILE,

        /**
         * Attachment is loaded from the classpath
         */
        RESOURCE;
    }

    private class Attachment {
        private final AttachmentType type;
        private final String path;
        private final String id;

        public Attachment(AttachmentType type, String path, String id) {
            notNull(type, "AttachmentType can not be null");
            notEmpty(path, "Attachment path can not be empty");

            this.type = type;
            this.path = path;
            this.id = id;
        }

        public MimeBodyPart getBodyPart() throws MessagingException {
            MimeBodyPart attachment = new MimeBodyPart();
            DataSource fds = null;
            switch (type) {
            case FILE:
                fds = new FileDataSource(path);
                attachment.setFileName(fds.getName());
                break;
            case RESOURCE:
                URL url = this.getClass().getClassLoader().getResource(path);
                if (url != null) {
                    fds = new URLDataSource(url);
                    String[] split = path.split("[/\\\\]");
                    attachment.setFileName(split[split.length - 1]);
                }
                break;
            }

            if (fds != null) {
                attachment.setDataHandler(new DataHandler(fds));
                if (id != null && !id.isEmpty())
                    attachment.setHeader("Content-ID", "<" + id + ">");
                return attachment;
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return String.format("Attachment[%s, path='%s', id='%s']\n", type.toString(), path, id);
        }
    }

    private class Authenticator extends javax.mail.Authenticator {
        private PasswordAuthentication authentication;

        public Authenticator() {
            authentication = new PasswordAuthentication(username, password);
        }

        protected PasswordAuthentication getPasswordAuthentication() {
            return authentication;
        }
    }

    private class Address {
        private final String email;
        private final String name;

        public Address(String email, String name) {
            notEmpty(email, "Email address can not be empty");

            this.email = email;
            this.name = name;
        }

        public InternetAddress getInternetAddress() throws MessagingException {
            try {
                if (name != null && !name.isEmpty()) {
                    return new InternetAddress(email, name);
                } else {
                    return new InternetAddress(email);
                }
            } catch (UnsupportedEncodingException e) {
                throw new MessagingException("Error creating email address", e);
            } catch (AddressException e) {
                throw new MessagingException("Error creating email address", e);
            }
        }

        @Override
        public String toString() {
            if (name != null && !name.isEmpty()) {
                return String.format("%s <%s>", name, email);
            } else {
                return email;
            }
        }
    }
}