org.yes.cart.service.mail.impl.MailComposerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.yes.cart.service.mail.impl.MailComposerImpl.java

Source

/*
 * Copyright 2009 Denys Pavlov, Igor Azarnyi
 *
 *    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 org.yes.cart.service.mail.impl;

import groovy.lang.Writable;
import groovy.text.GStringTemplateEngine;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.yes.cart.domain.entity.CustomerOrder;
import org.yes.cart.domain.entity.Mail;
import org.yes.cart.domain.entity.MailPart;
import org.yes.cart.service.mail.MailComposer;
import org.yes.cart.service.mail.MailTemplateResourcesProvider;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Class responsible to compose mail from given:
 * templates (txt and html);
 * set of variables;
 * current shop context;
 * current spring context.
 * <p/>
 * User: Igor Azarny iazarny@yahoo.com
 * Date: 09-May-2011
 * Time: 14:12:54
 */
public class MailComposerImpl implements MailComposer {

    private final Logger LOG = LoggerFactory.getLogger(MailComposerImpl.class);

    /**
     * Default regular expression.
     */
    private final static String RE_EXPRESSION = "cid\\:([^'\"]+)['|\"]";

    private String resourceExpression = RE_EXPRESSION;

    private Pattern resourcePattern;

    private final GStringTemplateEngine templateEngine;

    private final MailTemplateResourcesProvider mailTemplateResourcesProvider;

    /**
     * Construct mail composer
     *
     * @param mailTemplateResourcesProvider mail resources provider
     */
    public MailComposerImpl(final MailTemplateResourcesProvider mailTemplateResourcesProvider)
            throws ClassNotFoundException {
        this.mailTemplateResourcesProvider = mailTemplateResourcesProvider;
        final ClassLoader classLoader = this.getClass().getClassLoader();
        classLoader.loadClass(DecimalFormat.class.getName());
        this.templateEngine = new GStringTemplateEngine(classLoader);
    }

    /**
     * Merge model with template.
     *
     * @param view  groovy string template
     * @param model model
     * @return merged view.
     * @throws java.io.IOException    in case of inline resources can not be found
     * @throws ClassNotFoundException in case if something wrong with template engine
     */
    String merge(final String view, final Map<String, Object> model) throws IOException, ClassNotFoundException {
        final Writable writable = templateEngine.createTemplate(view).make(model);
        final StringWriter stringWriter = new StringWriter();
        writable.writeTo(stringWriter);
        stringWriter.close();
        return stringWriter.toString();
    }

    void composeMessage(final MimeMessage message, final String shopCode, final String locale,
            final List<String> mailTemplateChain, final String templateName, final String from,
            final String toEmail, final String ccEmail, final String bccEmail, final Map<String, Object> model)
            throws MessagingException, IOException, ClassNotFoundException {

        final MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

        helper.setTo(toEmail);

        helper.setSentDate(new Date());

        if (ccEmail != null) {
            helper.setCc(ccEmail);
        }

        if (bccEmail != null) {
            helper.setBcc(bccEmail);
        }

        final String textTemplate = getTemplate(mailTemplateChain, shopCode, locale, templateName, ".txt");
        final String htmlTemplate = getTemplate(mailTemplateChain, shopCode, locale, templateName, ".html");
        final String propString = getTemplate(mailTemplateChain, shopCode, locale, templateName, ".properties");
        final Properties prop = new Properties();
        if (propString != null) {
            prop.load(new StringReader(propString));
        }
        helper.setSubject(prop.getProperty("subject"));

        if (from == null) {
            helper.setFrom(prop.getProperty("from"));
        } else {
            helper.setFrom(from);
        }

        composeMessage(helper, textTemplate, htmlTemplate, mailTemplateChain, shopCode, locale, templateName,
                model);

    }

    /**
     * Fill mail message. At least one of the templates must be given.
     *
     * @param helper          mail message helper
     * @param textTemplate    optional text template
     * @param htmlTemplate    optional html template
     * @param mailTemplateChain path to template folder
     * @param shopCode        shop code
     * @param locale          locale
     * @param templateName    template name
     * @param model           model
     *
     * @throws MessagingException     in case if message can not be composed
     * @throws java.io.IOException    in case of inline resources can not be found
     * @throws ClassNotFoundException in case if something wrong with template engine
     */
    void composeMessage(final MimeMessageHelper helper, final String textTemplate, final String htmlTemplate,
            final List<String> mailTemplateChain, final String shopCode, final String locale,
            final String templateName, final Map<String, Object> model)
            throws MessagingException, ClassNotFoundException, IOException {

        if (textTemplate == null || htmlTemplate == null) {
            if (textTemplate != null) {
                helper.setText(merge(textTemplate, model), false);
            }

            if (htmlTemplate != null) {
                helper.setText(merge(htmlTemplate, model), true);
                inlineResources(helper, htmlTemplate, mailTemplateChain, shopCode, locale, templateName);
            }

        } else {
            helper.setText(merge(textTemplate, model), merge(htmlTemplate, model));
            inlineResources(helper, htmlTemplate, mailTemplateChain, shopCode, locale, templateName);
        }

    }

    /**
     * Add inline resource to mail message.
     * Resource id will be interpreted as file name in following fashion: filename_ext.
     *
     * @param helper          MimeMessageHelper, that has mail message
     * @param htmlTemplate    html message template
     * @param mailTemplateChain physical path to resources
     * @param shopCode        shop code
     * @param locale          locale
     * @param templateName    template name
     *
     * @throws javax.mail.MessagingException in case if resource can not be inlined
     */
    void inlineResources(final MimeMessageHelper helper, final String htmlTemplate,
            final List<String> mailTemplateChain, final String shopCode, final String locale,
            final String templateName) throws MessagingException, IOException {

        if (StringUtils.isNotBlank(htmlTemplate)) {
            final List<String> resourcesIds = getResourcesId(htmlTemplate);
            if (!resourcesIds.isEmpty()) {
                for (String resourceId : resourcesIds) {
                    final String resourceFilename = transformResourceIdToFileName(resourceId);
                    final byte[] content = mailTemplateResourcesProvider.getResource(mailTemplateChain, shopCode,
                            locale, templateName, resourceFilename);
                    helper.addInline(resourceId, new ByteArrayResource(content) {
                        @Override
                        public String getFilename() {
                            return resourceFilename;
                        }
                    });
                }
            }
        }

    }

    /**
     * Transform resource id in filename_extension format to filename.extension
     *
     * @param resourceName resource id
     * @return filename
     */
    String transformResourceIdToFileName(final String resourceName) {
        return resourceName.replace('_', '.');
    }

    /**
     * Collect resource ids
     *
     * @param htmlTemplate given html template
     * @return list of resource ids in template order.
     */
    List<String> getResourcesId(final String htmlTemplate) {
        final List<String> resourceIds = new ArrayList<String>();
        final Matcher matcher = getResourcePattern().matcher(htmlTemplate);
        while (matcher.find()) {
            resourceIds.add(matcher.group(1));
        }
        return resourceIds;
    }

    /**
     * Get regular expression to collect resources to inline.
     *
     * @return regular expression
     */
    public String getResourceExpression() {
        return resourceExpression;
    }

    /**
     * Set regular expression  to collect resources to inline.
     * Also set pattern to null.
     *
     * @param resourceExpression regular expression
     */
    public void setResourceExpression(final String resourceExpression) {
        this.resourceExpression = resourceExpression;
        this.resourcePattern = null;
    }

    private Pattern getResourcePattern() {
        if (resourcePattern == null) {
            resourcePattern = Pattern.compile(getResourceExpression(), Pattern.CASE_INSENSITIVE);
        }
        return resourcePattern;
    }

    /**
     * Get template as string.
     *
     * @param mailTemplateChain path to template folder
     * @param shopCode          shop code
     * @param locale            locale
     * @param fileName          file name
     * @param ext               file extension
     *
     * @return template if exists
     */
    String getTemplate(final List<String> mailTemplateChain, final String shopCode, final String locale,
            final String fileName, final String ext) {

        try {
            return mailTemplateResourcesProvider.getTemplate(mailTemplateChain, shopCode, locale, fileName, ext);
        } catch (IOException e) {
            LOG.warn("No template found for locale {}, template: {}, ext: {}",
                    new Object[] { locale, fileName, ext });
            return null;
        }

    }

    /** {@inheritDoc} */
    @Override
    public void composeMessage(final Mail mail, final String shopCode, final String locale,
            final List<String> mailTemplateChain, final String templateName, final String from,
            final String toEmail, final String ccEmail, final String bccEmail, final Map<String, Object> model)
            throws MessagingException, IOException, ClassNotFoundException {

        mail.setShopCode(shopCode);
        mail.setRecipients(toEmail);

        if (ccEmail != null) {
            mail.setCc(ccEmail);
        }

        if (bccEmail != null) {
            mail.setBcc(bccEmail);
        }

        final String textTemplate = getTemplate(mailTemplateChain, shopCode, locale, templateName, ".txt");
        final String htmlTemplate = getTemplate(mailTemplateChain, shopCode, locale, templateName, ".html");
        final String propString = getTemplate(mailTemplateChain, shopCode, locale, templateName, ".properties");
        final Properties prop = new Properties();
        if (propString != null) {

            prop.load(new StringReader(propString));

        }

        if (model.get("root") instanceof CustomerOrder) {
            mail.setSubject(((CustomerOrder) model.get("root")).getOrdernum() + ": " + prop.getProperty("subject"));
        } else {
            mail.setSubject(prop.getProperty("subject"));
        }

        if (from == null) {
            mail.setFrom(prop.getProperty("from"));
        } else {
            mail.setFrom(from);
        }

        composeMessage(mail, textTemplate, htmlTemplate, mailTemplateChain, shopCode, locale, templateName, model);

    }

    /**
     * Fill mail message. At least one of the templates must be given.
     *
     * @param mail            mail message
     * @param textTemplate    optional text template
     * @param htmlTemplate    optional html template
     * @param mailTemplateChain path to template folder
     * @param shopCode        shop code
     * @param locale          locale
     * @param templateName    template name
     * @param model           model
     *
     * @throws MessagingException     in case if message can not be composed
     * @throws java.io.IOException    in case of inline resources can not be found
     * @throws ClassNotFoundException in case if something wrong with template engine
     */
    void composeMessage(final Mail mail, final String textTemplate, final String htmlTemplate,
            final List<String> mailTemplateChain, final String shopCode, final String locale,
            final String templateName, final Map<String, Object> model)
            throws MessagingException, ClassNotFoundException, IOException {

        if (textTemplate == null || htmlTemplate == null) {
            if (textTemplate != null) {
                mail.setTextVersion(merge(textTemplate, model));
            }
            if (htmlTemplate != null) {
                mail.setHtmlVersion(merge(htmlTemplate, model));
                inlineResources(mail, htmlTemplate, mailTemplateChain, shopCode, locale, templateName);
            }

        } else {
            mail.setTextVersion(merge(textTemplate, model));
            mail.setHtmlVersion(merge(htmlTemplate, model));
            inlineResources(mail, htmlTemplate, mailTemplateChain, shopCode, locale, templateName);
        }

    }

    /**
     * Add inline resource to mail message.
     * Resource id will be interpreted as file name in following fashion: filename_ext.
     *
     * @param mail            MimeMessageHelper, that has mail message
     * @param htmlTemplate    html message template
     * @param mailTemplateChain path to template folder
     * @param shopCode          shop code
     * @param locale          locale
     * @param templateName    template name
     *
     * @throws javax.mail.MessagingException in case if resource can not be inlined
     */
    void inlineResources(final Mail mail, final String htmlTemplate, final List<String> mailTemplateChain,
            final String shopCode, final String locale, final String templateName)
            throws MessagingException, IOException {

        if (StringUtils.isNotBlank(htmlTemplate)) {
            final List<String> resourcesIds = getResourcesId(htmlTemplate);
            if (!resourcesIds.isEmpty()) {
                for (String resourceId : resourcesIds) {
                    final String resourceFilename = transformResourceIdToFileName(resourceId);
                    final byte[] content = mailTemplateResourcesProvider.getResource(mailTemplateChain, shopCode,
                            locale, templateName, resourceFilename);
                    final MailPart part = mail.addPart();
                    part.setResourceId(resourceId);
                    part.setFilename(resourceFilename);
                    part.setData(content);
                }
            }
        }

    }

    /** {@inheritDoc} */
    @Override
    public void convertMessage(final Mail mail, final MimeMessage mimeMessage)
            throws MessagingException, IOException, ClassNotFoundException {

        final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");

        helper.setTo(mail.getRecipients());

        helper.setSentDate(new Date());

        if (mail.getCc() != null) {
            helper.setCc(mail.getCc());
        }

        if (mail.getBcc() != null) {
            helper.setBcc(mail.getBcc());
        }

        final String textTemplate = mail.getTextVersion();
        final String htmlTemplate = mail.getHtmlVersion();

        helper.setSubject(mail.getSubject());
        helper.setFrom(mail.getFrom());

        if (textTemplate == null || htmlTemplate == null) {
            if (textTemplate != null) {
                helper.setText(textTemplate, false);
            }

            if (htmlTemplate != null) {
                helper.setText(htmlTemplate, true);
                inlineResources(helper, mail);
            }

        } else {
            helper.setText(textTemplate, htmlTemplate);
            inlineResources(helper, mail);
        }

    }

    /**
     * Add inline resource to mail message.
     * Resource id will be interpreted as file name in following fashion: filename_ext.
     *
     * @param helper          MimeMessageHelper, that has mail message
     * @param mail            html message template
     *
     * @throws javax.mail.MessagingException in case if resource can not be inlined
     */
    void inlineResources(final MimeMessageHelper helper, final Mail mail) throws MessagingException {

        if (CollectionUtils.isNotEmpty(mail.getParts())) {
            for (final MailPart part : mail.getParts()) {
                final String fileName = part.getFilename();
                final String resourceId = part.getResourceId();
                helper.addInline(resourceId, new ByteArrayResource(part.getData()) {
                    @Override
                    public String getFilename() {
                        return fileName;
                    }
                });
            }
        }

    }

}