nl.strohalm.cyclos.utils.MailHandler.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.utils.MailHandler.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos 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 2 of the License, or
(at your option) any later version.
    
Cyclos 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 Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.utils;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.members.Operator;
import nl.strohalm.cyclos.entities.members.PendingEmailChange;
import nl.strohalm.cyclos.entities.members.PendingMember;
import nl.strohalm.cyclos.entities.members.messages.MessageCategory;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.entities.settings.MailSettings;
import nl.strohalm.cyclos.entities.settings.MailTranslation;
import nl.strohalm.cyclos.entities.settings.MessageSettings;
import nl.strohalm.cyclos.exceptions.MailSendingException;
import nl.strohalm.cyclos.services.settings.SettingsServiceLocal;
import nl.strohalm.cyclos.utils.transaction.CurrentTransactionData;
import nl.strohalm.cyclos.utils.transaction.TransactionCommitListener;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

/**
 * Handles mail sending
 * @author luis
 */
public class MailHandler implements InitializingBean, DisposableBean {

    private class MailParameters {
        private String subject;
        private InternetAddress replyTo;
        private InternetAddress to;
        private String body;
        private boolean isHTML;
    }

    private class SenderThreads extends WorkerThreads<MailParameters> {

        public SenderThreads(final String name, final int threadCount) {
            super(name, threadCount);
        }

        @Override
        protected void process(final MailParameters params) {
            doSend(params.subject, params.replyTo, params.to, params.body, params.isHTML, true);
        }
    }

    private SettingsServiceLocal settingsService;
    private LinkGenerator linkGenerator;
    private MessageResolver messageResolver = new MessageResolver.NoOpMessageResolver();
    private SenderThreads senderThreads;
    private int maxThreads = 5;

    @Override
    public void afterPropertiesSet() throws Exception {
        senderThreads = new SenderThreads("Cyclos mail sender", maxThreads);
    }

    @Override
    public void destroy() throws Exception {
        if (senderThreads != null) {
            senderThreads.interrupt();
            senderThreads = null;
        }
    }

    /**
     * Returns the mail address for the given Element
     */
    public InternetAddress getInternetAddress(final Element element) {
        if (element == null) {
            return null;
        }
        return getInternetAddress(element.getName(), element.getEmail());
    }

    /**
     * Returns the mail address for the given PendingMember
     */
    public InternetAddress getInternetAddress(final PendingMember pendingMember) {
        if (pendingMember == null) {
            return null;
        }
        return getInternetAddress(pendingMember.getName(), pendingMember.getEmail());
    }

    /**
     * Returns the mail address for the given Element
     */
    public InternetAddress getInternetAddress(String name, final String email) {
        if (StringUtils.isEmpty(email)) {
            return null;
        }
        final LocalSettings localSettings = settingsService.getLocalSettings();
        if (StringUtils.isEmpty(name)) {
            name = email;
        }
        try {
            return new InternetAddress(email, name, localSettings.getCharset());
        } catch (final UnsupportedEncodingException e) {
            // Shouldn't happen!!!
            return null;
        }
    }

    /**
     * Returns the mail address
     */
    public InternetAddress getMailAddress(final String mail) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        try {
            return new InternetAddress(mail, mail, localSettings.getCharset());
        } catch (final UnsupportedEncodingException e) {
            // Shouldn't happen!!!
            return null;
        }
    }

    /**
     * Returns the mail address for the given element, but returns null when a member hides his mail
     */
    public InternetAddress getReplyAddress(Element element) {
        // When an operator, check his member
        if (element instanceof Operator) {
            element = ((Operator) element).getMember();
        }
        // Check whether the mail is hidden
        if (element instanceof Member) {
            final Member member = (Member) element;
            if (member.isHideEmail()) {
                return null;
            }
        }
        return getInternetAddress(element);
    }

    /**
     * Returns the mail address for system
     */
    public InternetAddress getSystemAddress() {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final MailSettings mailSettings = settingsService.getMailSettings();
        try {
            return new InternetAddress(mailSettings.getFromMail(), localSettings.getApplicationName(),
                    localSettings.getCharset());
        } catch (final UnsupportedEncodingException e) {
            // Shouldn't happen!!!
            return null;
        }
    }

    /**
     * Process the body of a message that's being sent by mail, appending the from member, the category and a suffix
     */
    public String processBody(final Element owner, final String body, final Member member,
            final MessageCategory category, final boolean isHtml) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final String lineSep = isHtml ? "<br>" : "\n";
        final StringBuilder sb = new StringBuilder();
        // When there is a member, it's from member (obviously). When there's a category, from the administration.
        // When neither, it's automatically generated from system
        boolean appendExtraLine = false;
        if (member != null || category != null) {
            sb.append(processLabel("message.from", isHtml));
            if (member == null) {
                sb.append(localSettings.getApplicationUsername());
            } else {
                sb.append(member.getName()).append(" (").append(member.getUsername()).append(")");
            }
            sb.append(lineSep);
            appendExtraLine = true;
        }
        // Append the category, if any
        if (category != null) {
            sb.append(processLabel("message.category", isHtml));
            sb.append(category.getName());
            sb.append(lineSep);
            appendExtraLine = true;
        }
        if (appendExtraLine) {
            sb.append(lineSep);
        }
        sb.append(body);
        sb.append(lineSep);
        sb.append(lineSep);
        final MessageSettings messageSettings = settingsService.getMessageSettings();

        // Append and process the suffix
        String suffix;
        if (isHtml) {
            suffix = messageSettings.getMessageMailSuffixHtml();
        } else {
            suffix = messageSettings.getMessageMailSuffixPlain();
        }
        final Map<String, String> variables = new HashMap<String, String>();
        variables.put("link", getRootUrl(owner, isHtml, true));
        variables.put("system_name", localSettings.getApplicationName());
        sb.append(MessageProcessingHelper.processVariables(suffix, variables));

        return sb.toString();
    }

    /**
     * Process the body of a message that's being sent by mail, appending the prefix
     */
    public String processSubject(final Element owner, final String subject) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final MessageSettings messageSettings = settingsService.getMessageSettings();

        String prefix = StringUtils.trimToEmpty(messageSettings.getMessageMailSubjectPrefix());
        final Map<String, String> variables = new HashMap<String, String>();
        variables.put("link", getRootUrl(owner, false, true));
        variables.put("system_name", localSettings.getApplicationName());
        prefix = MessageProcessingHelper.processVariables(prefix, variables);

        return StringUtils.trimToEmpty(prefix + " " + subject);
    }

    /**
     * Sends an arbitrary mail message
     */
    public void send(final String subject, final InternetAddress replyTo, final InternetAddress to,
            final String body, final boolean isHTML) {
        doSend(subject, replyTo, to, body, isHTML, false);
    }

    public void sendActivation(final boolean threaded, final Member member, final String password) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final Map<String, Object> variableValues = member.getVariableValues(localSettings);
        variableValues.put("password", password);
        variableValues.put("system_name", localSettings.getApplicationName());
        variableValues.put("link", getRootUrl(member, true, false));
        variableValues.put("url", getRootUrl(member, true, true));

        final MailTranslation mailTranslation = settingsService.getMailTranslation();
        final String subject = mailTranslation.getActivationSubject();
        String body;
        if (password == null) {
            body = mailTranslation.getActivationMessageWithoutPassword();
        } else {
            body = mailTranslation.getActivationMessageWithPassword();
        }
        sendInternal(threaded, getInternetAddress(member), variableValues, subject, body);
    }

    /**
     * Sends an email only after the current transaction is committed
     */
    public void sendAfterTransactionCommit(final String subject, final InternetAddress replyTo,
            final InternetAddress to, final String body, final boolean isHTML) {
        if (senderThreads == null) {
            return;
        }
        CurrentTransactionData.addTransactionCommitListener(new TransactionCommitListener() {
            @Override
            public void onTransactionCommit() {
                final MailParameters params = new MailParameters();
                params.subject = subject;
                params.replyTo = replyTo;
                params.to = to;
                params.body = body;
                params.isHTML = isHTML;
                senderThreads.enqueue(params);
            }
        });
    }

    public void sendEmailChange(final PendingEmailChange pendingEmailChange) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final Map<String, Object> variableValues = pendingEmailChange.getVariableValues(localSettings);
        variableValues.put("system_name", localSettings.getApplicationName());
        variableValues.put("link", linkGenerator.generateLinkForMailChangeValidation(pendingEmailChange));
        variableValues.put("url", linkGenerator.getMailChangeValidationUrl(pendingEmailChange));

        final MailTranslation mailTranslation = settingsService.getMailTranslation();
        final String subject = mailTranslation.getMailChangeValidationSubject();
        final String body = mailTranslation.getMailChangeValidationMessage();

        Member member = pendingEmailChange.getMember();
        InternetAddress internetAddress = getInternetAddress(member.getName(), pendingEmailChange.getNewEmail());
        sendInternal(false, internetAddress, variableValues, subject, body);
    }

    public void sendEmailValidation(final PendingMember pendingMember) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final Map<String, Object> variableValues = pendingMember.getVariableValues(localSettings);
        variableValues.put("system_name", localSettings.getApplicationName());
        final String key = pendingMember.getValidationKey();
        variableValues.put("link",
                linkGenerator.generateLinkForMailValidation(pendingMember.getMemberGroup(), key));
        variableValues.put("url", linkGenerator.getMailValidationUrl(pendingMember.getMemberGroup(), key));

        final MailTranslation mailTranslation = settingsService.getMailTranslation();
        final String subject = mailTranslation.getMailValidationSubject();
        final String body = mailTranslation.getMailValidationMessage();

        sendInternal(false, getInternetAddress(pendingMember), variableValues, subject, body);
    }

    public void sendInvitation(final Element fromElement, final String toMail) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final Map<String, Object> variableValues = fromElement.getVariableValues(localSettings);
        variableValues.put("system_name", localSettings.getApplicationName());
        variableValues.put("link", getRootUrl(fromElement, true, true));

        final MailTranslation mailTranslation = settingsService.getMailTranslation();
        final String subject = mailTranslation.getInvitationSubject();
        final String body = mailTranslation.getInvitationMessage();

        sendInternal(false, getMailAddress(toMail), variableValues, subject, body);
    }

    public void sendResetPassword(final Member member, final String password) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final Map<String, Object> variableValues = member.getVariableValues(localSettings);
        variableValues.put("password", password);
        variableValues.put("system_name", localSettings.getApplicationName());
        variableValues.put("link", getRootUrl(member, true, true));

        final MailTranslation mailTranslation = settingsService.getMailTranslation();
        final String subject = mailTranslation.getResetPasswordSubject();
        final String body = mailTranslation.getResetPasswordMessage();

        sendInternal(false, getInternetAddress(member), variableValues, subject, body);
    }

    public void setLinkGenerator(final LinkGenerator linkGenerator) {
        this.linkGenerator = linkGenerator;
    }

    public void setMaxThreads(final int maxThreads) {
        this.maxThreads = maxThreads;
    }

    public void setMessageResolver(final MessageResolver messageResolver) {
        this.messageResolver = messageResolver;
    }

    public void setSettingsServiceLocal(final SettingsServiceLocal settingsService) {
        this.settingsService = settingsService;
    }

    private void doSend(final String subject, final InternetAddress replyTo, final InternetAddress to,
            final String body, final boolean isHTML, final boolean throwException) {
        if (to == null || StringUtils.isEmpty(to.getAddress())) {
            return;
        }
        final LocalSettings localSettings = settingsService.getLocalSettings();
        final MailSettings mailSettings = settingsService.getMailSettings();
        final JavaMailSender mailSender = mailSettings.getMailSender();
        final MimeMessage message = mailSender.createMimeMessage();
        final MimeMessageHelper helper = new MimeMessageHelper(message, localSettings.getCharset());

        try {
            helper.setFrom(getSystemAddress());
            if (replyTo != null) {
                helper.setReplyTo(replyTo);
            }
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(body, isHTML);
            mailSender.send(message);
        } catch (final MessagingException e) {
            if (throwException) {
                throw new MailSendingException(subject, e);
            }
            // Store the current Exception
            CurrentTransactionData.setMailError(new MailSendingException(subject, e));
        } catch (final MailException e) {
            if (throwException) {
                throw new MailSendingException(subject, e);
            }
            CurrentTransactionData.setMailError(new MailSendingException(subject, e));
        }
    }

    private String getRootUrl(final Element element, final boolean isHtml, final boolean useUrlAsLink) {
        if (linkGenerator == null) {
            return "";
        }
        final String rootUrl = linkGenerator.getRootUrl(element);
        if (isHtml) {
            if (useUrlAsLink) {
                return "<a href='" + rootUrl + "'>" + rootUrl + "</a>";
            } else {
                return linkGenerator.generateForApplicationRoot(element);
            }
        } else {
            return rootUrl;
        }
    }

    private String processLabel(final String key, final boolean isHtml) {
        String prefix = "";
        String suffix = "";
        if (isHtml) {
            prefix = "<b>";
            suffix = "</b>";
        }
        return prefix + messageResolver.message(key) + ":" + suffix + " ";
    }

    private void sendInternal(final boolean threaded, final InternetAddress to, final Map<String, Object> variables,
            final String subject, final String body) {
        final String processedSubject = MessageProcessingHelper.processVariables(subject, variables);
        final String processedBody = MessageProcessingHelper.processVariables(body, variables);
        if (threaded) {
            sendAfterTransactionCommit(processedSubject, null, to, processedBody, true);
        } else {
            send(processedSubject, null, to, processedBody, true);
        }
    }

}