All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.enterprisemath.utils.messaging.SmtpMessenger Maven / Gradle / Ivy

package com.enterprisemath.utils.messaging;

import java.net.ConnectException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.mail.Address;
import javax.mail.Authenticator;
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.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.velocity.app.VelocityEngine;

import com.enterprisemath.utils.DomainUtils;
import com.enterprisemath.utils.ValidationUtils;
import java.io.StringWriter;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.util.ByteArrayDataSource;
import org.apache.commons.codec.binary.Base64;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.VelocityException;

/**
 * Messenger implementation which sends messages through SMTP channel.
 *
 * @author radek.hecl
 *
 */
public class SmtpMessenger implements Messenger {

    /**
     * SMTP server.
     */
    private String smtpHost;

    /**
     * SMTP server port.
     */
    private Integer smtpPort;

    /**
     * Security which is used to transport email.
     */
    private TransportSecurity transportSecurity;

    /**
     * User name for login to SMTP server.
     */
    private String username;

    /**
     * Password for login to SMTP server.
     */
    private String password;

    /**
     * Port for the socket.
     */
    private Integer socketFactoryPort;

    /**
     * Provider for templates.
     */
    private MessageTemplateProvider templateProvider;

    /**
     * Model provided by default to the
     */
    private Map defaultModel;

    /**
     * Velocity engine for formatting messages.
     */
    private VelocityEngine velocityEngine;

    /**
     * Creates new instance.
     */
    private SmtpMessenger() {
    }

    /**
     * Guards this object to be consistent. Throws exception if this is not the case.
     */
    @PostConstruct
    private void guardInvariants() {
        ValidationUtils.guardNotEmpty(smtpHost, "smtpHost cannot be empty");
        ValidationUtils.guardNotNull(smtpPort, "smtpPort cannot be null");
        ValidationUtils.guardNotNull(transportSecurity, "transportSecurity cannot be null");
        if (transportSecurity.equals(TransportSecurity.NONE)) {
            ValidationUtils.guardEmpty(username, "username must be empty for non secured transport");
            ValidationUtils.guardEmpty(password, "password must be empty for non secured transport");
        }
        else {
            ValidationUtils.guardNotEmpty(username, "username cannot be empty for secured transport");
            ValidationUtils.guardNotEmpty(password, "password cannot be empty for secured transport");
        }
        if (transportSecurity.equals(TransportSecurity.SSL)) {
            ValidationUtils.guardNotNull(socketFactoryPort, "socketFactoryPort cannot be null for SSL transport");
        }
        else {
            ValidationUtils.guardNull(socketFactoryPort, "socketFactoryPort must be null for non SSL transport");
        }
        ValidationUtils.guardNotNull(templateProvider, "templateProvider cannot be null");
        ValidationUtils.guardNotEmptyNullMap(defaultModel, "defaultModel cannot have empty key or null value");
        ValidationUtils.guardNotNull(velocityEngine, "velocityEngine cannot be null");
    }

    @Override
    @SuppressWarnings("ThrowableResultIgnored")
    public void send(String target, Message message) {
        // format
        Map model = new HashMap();
        model.putAll(defaultModel);
        model.putAll(DomainUtils.convertPropertyMapIntoVelocityModel(message.getParameters()));
        String subject = applyTemplate(templateProvider.getTemplate(message.getType(), "subject"), model);
        String text = applyTemplate(templateProvider.getTemplate(message.getType(), "message"), model);

        // send
        try {
            boolean end = false;
            int count = 0;
            while (end == false) {
                try {
                    sendOneTry(target, subject, text, message.getAttachments());
                    end = true;
                } catch (MessagingException e) {
                    if (count >= 5 || !(e.getCause() instanceof ConnectException)) {
                        // kick out if retry was already enough or non retry able reason
                        throw e;
                    }
                    ++count;
                    try {
                        Thread.sleep(30000);
                    } catch (InterruptedException e1) {
                        throw new RuntimeException(e1);
                    }
                }
            }
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *
     * Sends single message. Makes only one try and throws exception if this fails.
     *
     * @param target target
     * @param subject subject
     * @param text message text
     * @param attchements attachments
     * @throws MessagingException in case of error during transport
     */
    private void sendOneTry(String target, String subject, String text, List attachments) throws MessagingException {
        // now send it
        Session session = createSession();

        // check whether there is a base64 encoded image and if yes, update the message and embed the image
        Map imageCids2Attachemnts = new HashMap();
        Pattern pattern = Pattern.compile(".*img.*src=\"(data:image/(png|gif);base64,([^\"]*)\").*", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(text);
        int i = 0;
        while (matcher.matches()) {
            String cid = "embeddedImage" + i;
            Attachment att = new Attachment.Builder().
                    setName(cid + "." + matcher.group(2)).
                    setMime("image/" + matcher.group(2)).
                    setBuf(Base64.decodeBase64(matcher.group(3))).
                    build();
            imageCids2Attachemnts.put(cid, att);
            text = text.substring(0, matcher.start(1)) + "cid:" + cid + text.substring(matcher.end(1) - 1);
            matcher = pattern.matcher(text);
            ++i;
        }

        // now create the message
        MimeMultipart multipart = new MimeMultipart();

        MimeBodyPart messageBodyPart = new MimeBodyPart();
        messageBodyPart.setText(text, "utf-8", "html");
        multipart.addBodyPart(messageBodyPart);

        // include standard attachments
        for (Attachment att : attachments) {
            MimeBodyPart attachmentBodyPart = new MimeBodyPart();
            DataSource source = new ByteArrayDataSource(att.getBuf(), att.getMime());
            attachmentBodyPart.setDataHandler(new DataHandler(source));
            attachmentBodyPart.setFileName(att.getName());
            multipart.addBodyPart(attachmentBodyPart);
        }

        // include embedded images as attachments
        for (Map.Entry emdImg : imageCids2Attachemnts.entrySet()) {
            MimeBodyPart attachmentBodyPart = new MimeBodyPart();
            DataSource source = new ByteArrayDataSource(emdImg.getValue().getBuf(), emdImg.getValue().getMime());
            attachmentBodyPart.setDataHandler(new DataHandler(source));
            attachmentBodyPart.setFileName(emdImg.getValue().getName());
            attachmentBodyPart.setContentID("<" + emdImg.getKey() + ">");
            attachmentBodyPart.setDisposition("inline");
            multipart.addBodyPart(attachmentBodyPart);
        }

        MimeMessage mimeMsg = new MimeMessage(session);
        mimeMsg.setSentDate(new Date());
        mimeMsg.setFrom(new InternetAddress("[email protected]"));
        mimeMsg.setReplyTo(new Address[]{new InternetAddress("[email protected]")});
        mimeMsg.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(target));
        mimeMsg.setSubject(subject);
        mimeMsg.setContent(multipart);
        mimeMsg.saveChanges();

        Transport.send(mimeMsg);
    }

    /**
     * Creates mail session.
     *
     * @return created session
     */
    private Session createSession() {
        Properties props = null;
        Session session = null;

        switch (transportSecurity) {
        case NONE:
            props = new Properties();
            props.put("mail.smtp.host", smtpHost);
            props.put("mail.smtp.port", Integer.toString(smtpPort));
            props.put("mail.from", "[email protected]");
            session = Session.getInstance(props, null);
            break;
        case SSL:
            props = new Properties();
            props.put("mail.smtp.host", smtpHost);
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.port", Integer.toString(smtpPort));
            props.put("mail.smtp.socketFactory.port", Integer.toString(socketFactoryPort));
            props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            session = Session.getInstance(props, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });
            break;
        case TLS:
            props = new Properties();
            props.put("mail.smtp.host", smtpHost);
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.port", Integer.toString(smtpPort));
            props.put("mail.smtp.starttls.enable", "true");
            session = Session.getInstance(props, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });
            break;
        default:
            throw new IllegalStateException("unsupported transport security - implement me if you need");
        }
        return session;
    }

    /**
     * Applies template to the model and returns result
     *
     * @param templateLocation template location
     * @param model model
     * @return result after operation is done
     */
    public String applyTemplate(String templateLocation, Map model) {
        StringWriter result = new StringWriter();
        try {
            VelocityContext velocityContext = new VelocityContext(model);
            velocityEngine.mergeTemplate(templateLocation, "utf-8", velocityContext, result);
        } catch (VelocityException e) {
            throw new RuntimeException(e);
        }
        return result.toString();
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    /**
     * Creates new instance.
     *
     * @param smtpHost SMTP server
     * @param smtpPort SMTP server port
     * @param transportSecurity security which is used to transport email
     * @param username user name for login to SMTP server
     * @param password password for login to SMTP server
     * @param socketFactoryPort port for the socket
     * @param templateProvider provider for message templates
     * @param defaultModel model accessible always in all templates
     * @param velocityEngine velocity engine
     * @return created instance
     */
    public static SmtpMessenger create(String smtpHost, Integer smtpPort, TransportSecurity transportSecurity,
            String username, String password, Integer socketFactoryPort, MessageTemplateProvider templateProvider,
            Map defaultModel, VelocityEngine velocityEngine) {
        SmtpMessenger res = new SmtpMessenger();
        res.smtpHost = smtpHost;
        res.smtpPort = smtpPort;
        res.transportSecurity = transportSecurity;
        res.username = username;
        res.password = password;
        res.socketFactoryPort = socketFactoryPort;
        res.templateProvider = templateProvider;
        res.defaultModel = DomainUtils.softCopyUnmodifiableMap(defaultModel);
        res.velocityEngine = velocityEngine;
        res.guardInvariants();
        return res;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy