EMailServiceImpl.java

/**
 * Copyright 2014 Global Crop Diversity Trust
 *
 * 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.genesys.server.service.impl;

import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.mail.internet.MimeMessage;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.genesys.server.exception.NotFoundElement;
import org.genesys.server.model.impl.Article;
import org.genesys.server.service.ArticleService;
import org.genesys.server.service.ArticleTranslationService;
import org.genesys.server.service.ContentService;
import org.genesys.server.service.EMailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.TaskExecutor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Service;

@Service
public class EMailServiceImpl implements EMailService, InitializingBean {

	private static final Logger LOG = LoggerFactory.getLogger(EMailServiceImpl.class);
	private static final String EMAIL_TEMPLATE_PATH = "/email/template.html";

	@Autowired
	private JavaMailSender mailSender;

	@Autowired
	private TaskExecutor taskExecutor;

	@Autowired
	private ArticleService articleService;
	
	@Autowired
	private ContentService contentService;

	@Value("${mail.async}")
	private boolean async;

	@Value("${mail.debug.message}")
	private String debugMessage;

	@Value("${mail.user.from}")
	private String emailFrom;

	@Value("${mail.disabled:false}")
	private boolean emailDisabled;

	@Value("${base.url}")
	private String baseUrl;

	@Value("${frontend.url}")
	private String frontendUrl;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		ArticleTranslationService.TranslatedArticle emailTemplate = null;
		try {
			emailTemplate = articleService.getGlobalArticle(ContentService.SMTP_EMAIL_TEMPLATE, Locale.ENGLISH);
		} catch (NotFoundElement e) {
			LOG.warn("{} email template article not found.", ContentService.SMTP_EMAIL_TEMPLATE);
		}
		if (emailTemplate == null || emailTemplate.entity == null) {
			try {
				String templateContent = Files.readString(Path.of(getClass().getResource(EMAIL_TEMPLATE_PATH).getPath()));
				Article article = new Article();
				article.setSlug(ContentService.SMTP_EMAIL_TEMPLATE);
				article.setOriginalLanguageTag(Locale.ENGLISH.getLanguage());
				article.setTitle("Email Template");
				article.setSummary("HTML template that is automatically applied to all emails");
				article.setBody(templateContent);
				article.setTemplate(true);
				article.setPublishDate(Instant.now());
				articleService.createFast(article);
			} catch (Exception e) {
				LOG.warn("An exception occurred while reading " + EMAIL_TEMPLATE_PATH, e);
			}
		}
	}
	
	@Override
	public String[] toEmails(String emailAddresses) {
		if (StringUtils.isBlank(emailAddresses)) {
			return null;
		}
		String[] res = emailAddresses.split(",|;");
		for (int i = 0; i < res.length; i++) {
			res[i] = StringUtils.trim(res[i]);
		}
		return res;
	}

	@Override
	public void sendMail(String mailSubject, String mailBody, String... emailTo) {
		sendMail(mailSubject, mailBody, null, emailTo);
	}

	@Override
	public void sendMail(String mailSubject, String mailBody, String[] emailCc, String... emailTo) {
		sendSimpleEmail(mailSubject, mailBody, emailFrom, emailCc, emailTo);
	}

	public void sendSimpleEmail(final String subject, final String text, final String emailFrom, final String[] emailCc, final String... emailTo) {
		printDebugInfo(subject, text, emailFrom, emailTo);

		final MimeMessagePreparator preparator = new MimeMessagePreparator() { // For debugging
			@Override
			public void prepare(MimeMessage mimeMessage) throws Exception {
				final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");

				message.setFrom(emailFrom);
				message.setTo(emailTo);
				LOG.info("Email to: {}", ArrayUtils.toString(emailTo, "<NULL>"));
				if (emailCc != null && emailCc.length > 0) {
					message.setCc(emailCc);
				}
				message.setSubject(subject);

				String messageText = text;
				try {
					final Map<String, Object> scopes = new HashMap<>();
					scopes.put("messageBody", text);
					scopes.put("subject", subject);
					scopes.put("frontendUrl", frontendUrl);
					scopes.put("baseUrl", baseUrl);
					scopes.put("serverName", "Genesys");

					ArticleTranslationService.TranslatedArticle article = articleService.getGlobalArticle(ContentService.SMTP_EMAIL_TEMPLATE, Locale.ENGLISH);
					if (article != null) {
						var body = article.getTranslation() != null ? article.getTranslation().getBody() : article.getEntity().getBody();
						messageText = contentService.processTemplate(body, scopes);
					}
				} catch (Exception e) {
					LOG.warn("Exception in applying email template", e);
				}

				message.setText(messageText, true);
			}
		};

		doSend(preparator);
	}

	protected void doSend(final MimeMessagePreparator preparator) {
		if (emailDisabled) {
			LOG.warn("Emailing is disabled");
			return;
		}
		// execute sender in separate thread
		JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl)mailSender;

		if (async) {
			LOG.info("Sending email message asynchroniously");
			taskExecutor.execute(() -> {
				try {
					LOG.warn("Sending email now via {}:{}", javaMailSender.getHost(), javaMailSender.getPort());
					mailSender.send(preparator);
					LOG.warn("Email delivered to {}:{}", javaMailSender.getHost(), javaMailSender.getPort());
				} catch (final Throwable e) {
					LOG.error("Error sending email: {}", e.getMessage());
				}
			});
		} else {
			LOG.warn("Sending email now via {}:{}", javaMailSender.getHost(), javaMailSender.getPort());
			mailSender.send(preparator);
		}
	}

	protected void printDebugInfo(String subject, String text, String emailFrom, String[] emailTo) {
		LOG.debug(getDebugString(subject, text, emailFrom, emailTo));
	}

	protected String getDebugString(String subject, String text, String emailFrom, String[] emailTo) {
		return String.format(debugMessage, emailFrom, Arrays.toString(emailTo), subject, text);
	}
}