com.gamesalutes.utils.email.DefaultEmailServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.gamesalutes.utils.email.DefaultEmailServiceImpl.java

Source

/*
 * Copyright (c) 2013 Game Salutes.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * Contributors:
 *     Game Salutes - Repackaging and modifications of original work under University of Chicago and Apache License 2.0 shown below
 * 
 * Repackaging from edu.uchicago.nsit.iteco.utils to com.gamesalutes.utils
 * 
 * Copyright 2008 - 2011 University of Chicago
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.gamesalutes.utils.email;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gamesalutes.utils.ByteBufferInputStream;
import com.gamesalutes.utils.ByteUtils;
import com.gamesalutes.utils.MiscUtils;
import com.gamesalutes.utils.StreamDataSource;
import com.gamesalutes.utils.StreamDataSource.DefaultStreamFactory;
import com.gamesalutes.utils.StreamDataSource.StreamFactory;

public class DefaultEmailServiceImpl implements EmailService {

    private static final int SOCKET_CONNECT_TIMEOUT_MS = 60 * 1000;
    private static final int SOCKET_IO_TIMEOUT_MS = 60 * 1000;

    private String host = "localhost";
    private int port = 25;

    private final static Logger LOGGER = LoggerFactory.getLogger(DefaultEmailServiceImpl.class.getName());

    public void setPort(Integer port) {
        this.port = port;
    }

    public void setHost(String host) {
        this.host = host;
    }

    private void setEmailRecipients(MimeMessage msg, List<String> addresses, RecipientType type)
            throws EmailException {
        InternetAddress[] inetAddr = new InternetAddress[addresses.size()];
        try {
            for (int i = 0; i < addresses.size(); ++i) {
                String addr = addresses.get(i);
                if (MiscUtils.isEmpty(addr))
                    throw new IllegalArgumentException("model.get" + type + " has empty address at index:" + i);
                addr = addr.trim();
                doValidateEmailAddress(addr);
                inetAddr[i] = new InternetAddress(addr);
            }

            msg.setRecipients(type, inetAddr);
        } catch (MessagingException e) {
            throw new EmailAddressException(e);
        }

    }

    private void doValidateEmailAddress(String address) throws EmailAddressException {
        if (address == null)
            throw new NullPointerException("address");

        int index = address.indexOf('@');
        if (index == -1)
            throw new EmailAddressException("address=" + address + ";missing '@'");
        if (index == 0)
            throw new EmailAddressException("address=" + address + ";missing user");
        if (index == address.length() - 1)
            throw new EmailAddressException("address=" + address + ";missing domain");
        int domainIndex = address.indexOf('.', index);
        if (domainIndex <= index + 1 || domainIndex == address.length() - 1)
            throw new EmailAddressException("address=" + address + ";bad domain");
    }

    public void send(EmailModel model) throws EmailException {

        if (LOGGER.isDebugEnabled())
            LOGGER.debug("Sending email: " + model);

        if (model == null)
            throw new NullPointerException("model");

        Properties emailProps;
        Session emailSession;

        // set the relay host as a property of the email session
        emailProps = new Properties();
        emailProps.setProperty("mail.transport.protocol", "smtp");
        emailProps.put("mail.smtp.host", host);
        emailProps.setProperty("mail.smtp.port", String.valueOf(port));
        // set the timeouts
        emailProps.setProperty("mail.smtp.connectiontimeout", String.valueOf(SOCKET_CONNECT_TIMEOUT_MS));
        emailProps.setProperty("mail.smtp.timeout", String.valueOf(SOCKET_IO_TIMEOUT_MS));

        if (LOGGER.isDebugEnabled())
            LOGGER.debug("Email properties: " + emailProps);

        // set up email session
        emailSession = Session.getInstance(emailProps, null);
        emailSession.setDebug(false);

        String from;
        String displayFrom;

        String body;
        String subject;
        List<EmailAttachment> attachments;

        if (model.getFrom() == null)
            throw new NullPointerException("from");
        if (MiscUtils.isEmpty(model.getTo()) && MiscUtils.isEmpty(model.getBcc())
                && MiscUtils.isEmpty(model.getCc()))
            throw new IllegalArgumentException("model has no addresses");

        from = model.getFrom();
        displayFrom = model.getDisplayFrom();
        body = model.getBody();
        subject = model.getSubject();
        attachments = model.getAttachments();

        MimeMessage emailMessage;
        InternetAddress emailAddressFrom;

        // create an email message from the current session
        emailMessage = new MimeMessage(emailSession);

        // set the from
        try {
            emailAddressFrom = new InternetAddress(from, displayFrom);
            emailMessage.setFrom(emailAddressFrom);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }

        if (!MiscUtils.isEmpty(model.getTo()))
            setEmailRecipients(emailMessage, model.getTo(), RecipientType.TO);
        if (!MiscUtils.isEmpty(model.getCc()))
            setEmailRecipients(emailMessage, model.getCc(), RecipientType.CC);
        if (!MiscUtils.isEmpty(model.getBcc()))
            setEmailRecipients(emailMessage, model.getBcc(), RecipientType.BCC);

        try {

            if (!MiscUtils.isEmpty(subject))
                emailMessage.setSubject(subject);

            Multipart multipart = new MimeMultipart();

            if (body != null) {
                // create the message part
                MimeBodyPart messageBodyPart = new MimeBodyPart();

                //fill message
                String bodyContentType;
                //        body = Utils.base64Encode(body);
                bodyContentType = "text/html; charset=UTF-8";

                messageBodyPart.setContent(body, bodyContentType);
                //Content-Transfer-Encoding : base64
                //        messageBodyPart.addHeader("Content-Transfer-Encoding", "base64");
                multipart.addBodyPart(messageBodyPart);
            }
            // Part two is attachment
            if (attachments != null && !attachments.isEmpty()) {
                try {
                    for (EmailAttachment a : attachments) {
                        MimeBodyPart attachBodyPart = new MimeBodyPart();
                        // don't base 64 encode
                        DataSource source = new StreamDataSource(
                                new DefaultStreamFactory(a.getInputStream(), false), a.getName(),
                                a.getContentType());
                        attachBodyPart.setDataHandler(new DataHandler(source));
                        attachBodyPart.setFileName(a.getName());
                        attachBodyPart.setHeader("Content-Type", a.getContentType());
                        attachBodyPart.addHeader("Content-Transfer-Encoding", "base64");

                        // add the attachment to the message
                        multipart.addBodyPart(attachBodyPart);

                    }
                }
                // close all the input streams
                finally {
                    for (EmailAttachment a : attachments)
                        MiscUtils.closeStream(a.getInputStream());
                }
            }

            // set the content
            emailMessage.setContent(multipart);
            emailMessage.saveChanges();

            if (LOGGER.isDebugEnabled())
                LOGGER.debug("Sending email message: " + emailMessage);

            Transport.send(emailMessage);

            if (LOGGER.isDebugEnabled())
                LOGGER.debug("Sending email complete.");

        } catch (Exception e) {
            throw new EmailException(e);
        }

    }

    /////////////////// INNER CLASSES /////////////////////////////////////////////////////////
    /**
     * Creates a new input stream and new output stream on demand that
     * encapsulates the desired content.
     * 
     * @author Justin Montgomery
     */
    private interface StreamFactory {
        InputStream newInputStream(String name) throws IOException;

        OutputStream newOutputStream(String name) throws IOException;
    }

    /**
     * <code>DataSource</code> for an arbitrary stream.  MIME type will be "application/octet-stream" unless
     * indicated otherwise.
     * 
     * @author Justin Montgomery
     */
    private static class StreamDataSource implements DataSource {

        private final StreamFactory creator;
        private final String name;
        private final String contentType;

        public StreamDataSource(StreamFactory creator, String name) {
            this(creator, name, null);
        }

        public StreamDataSource(StreamFactory creator, String name, String contentType) {
            if (creator == null)
                throw new NullPointerException("creator");
            this.creator = creator;
            this.name = name;
            if (contentType != null)
                this.contentType = contentType;
            else
                this.contentType = "application/octet-stream";
        }

        /* (non-Javadoc)
         * @see javax.activation.DataSource#getContentType()
         */
        public String getContentType() {
            return contentType;
        }

        /* (non-Javadoc)
         * @see javax.activation.DataSource#getInputStream()
         */
        public InputStream getInputStream() throws IOException {
            return creator.newInputStream(name);
        }

        /* (non-Javadoc)
         * @see javax.activation.DataSource#getName()
         */
        public String getName() {
            return name;
        }

        /* (non-Javadoc)
         * @see javax.activation.DataSource#getOutputStream()
         */
        public OutputStream getOutputStream() throws IOException {
            return creator.newOutputStream(name);
        }
    }

    /**
     * Default implementation of stream creator that only supports input streams and stores its content in
     * memory so that {@link #newInputStream(String)} can return an input stream that points to the position of the
     * content at the time this object was constructed.
     * 
     * @author Justin Montgomery
     */
    private static class DefaultStreamFactory implements StreamFactory {
        private ByteBuffer data;
        private InputStream in;

        public DefaultStreamFactory(InputStream in, boolean base64Encode) throws IOException {
            if (in == null)
                throw new NullPointerException("in");

            if (base64Encode) {
                this.data = createBuffer(in, true);
            } else if (!(in instanceof ByteArrayInputStream) || !in.markSupported()) {
                this.data = createBuffer(in, false);
            }
            // ByteArrayInputStream do not create another copy
            else {
                this.in = in;
                in.mark(in.available());

            }
        }

        private ByteBuffer createBuffer(InputStream in, boolean base64) throws IOException {

            byte[] data = ByteUtils.readBytes(in);

            if (base64) {
                try {
                    data = Base64.encodeBase64(data);
                } catch (Exception e) {
                    IOException ioe = new IOException(e.getMessage());
                    ioe.initCause(e);
                    throw ioe;
                }
            }

            return ByteBuffer.wrap(data);
        }

        private ByteBuffer getBuffer() throws IOException {
            ByteBuffer b = data.duplicate();
            b.rewind();
            return b;
        }

        /* (non-Javadoc)
         * @see com.gamesalutes.utils.StreamDataSource.StreamCreator#newInputStream(java.lang.String)
         */
        public synchronized InputStream newInputStream(String name) throws IOException {
            if (in != null) {
                in.reset();
                return in;
            }
            return new ByteBufferInputStream(getBuffer());
        }

        /* (non-Javadoc)
         * @see com.gamesalutes.utils.StreamDataSource.StreamCreator#newOutputStream(java.lang.String)
         */
        public OutputStream newOutputStream(String name) {
            throw new UnsupportedOperationException();
        }

    }

    public void validate(EmailModel model) throws EmailException {
        if (model == null)
            throw new NullPointerException("model");

        doValidateEmailAddress(model.getFrom());
        if (MiscUtils.isEmpty(model.getTo()) && MiscUtils.isEmpty(model.getBcc())
                && MiscUtils.isEmpty(model.getCc()))
            throw new IllegalArgumentException("model has no addresses");

        doValidateEmailAddresses(model.getTo());
        doValidateEmailAddresses(model.getCc());
        doValidateEmailAddresses(model.getBcc());
    }

    private void doValidateEmailAddresses(List<String> addresses) throws EmailAddressException {
        if (!MiscUtils.isEmpty(addresses)) {
            for (String addr : addresses)
                doValidateEmailAddress(addr);
        }

    }

    public boolean validateEmailAddress(String address) {
        try {
            doValidateEmailAddress(address);
            return true;
        } catch (EmailAddressException e) {
            return false;
        }
    }

}