package com.sungardas.enhancedsnapshots.service.impl;
import com.sungardas.enhancedsnapshots.aws.dynamodb.model.MailConfigurationDocument;
import com.sungardas.enhancedsnapshots.aws.dynamodb.model.TaskEntry;
import com.sungardas.enhancedsnapshots.components.ConfigurationMediator;
import com.sungardas.enhancedsnapshots.dto.MailConfigurationDto;
import com.sungardas.enhancedsnapshots.exception.ConfigurationException;
import com.sungardas.enhancedsnapshots.exception.EmailNotificationException;
import com.sungardas.enhancedsnapshots.service.CryptoService;
import com.sungardas.enhancedsnapshots.service.MailService;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.util.*;
@Service
public class MailServiceImpl implements MailService {
private static final Logger LOG = LogManager.getLogger(MailServiceImpl.class);
@Value("${enhancedsnapshots.mail.success.template.path}")
private String successTemplatePath;
@Value("${enhancedsnapshots.mail.success.subject}")
private String successSubject;
@Value("${enhancedsnapshots.mail.error.template.path}")
private String failTemplatePath;
@Value("${enhancedsnapshots.mail.error.subject}")
private String errorSubject;
@Value("${enhancedsnapshots.mail.info.template.path}")
private String systemInformationTemplatePath;
@Value("${enhancedsnapshots.mail.info.subject}")
private String infoSubject;
@Value("${enhancedsnapshots.mail.test.message.subject}")
private String testSubject;
@Value("${enhancedsnapshots.mail.test.message}")
private String testMessage;
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private ConfigurationMediator configurationMediator;
@Autowired
private CryptoService cryptoService;
private Template successTemplate;
private Template failTemplate;
private Template infoTemplate;
private Session session;
@PostConstruct
private void init() throws IOException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_25);
cfg.setTemplateLoader(new TemplateLoader() {
@Override
public Object findTemplateSource(String name) throws IOException {
Resource resource = resourceLoader.getResource(name);
return resource.exists() ? resource : null;
}
@Override
public long getLastModified(Object templateSource) {
Resource resource = (Resource) templateSource;
try {
return resource.lastModified();
} catch (IOException e) {
return 0;
}
}
@Override
public Reader getReader(Object templateSource, String encoding) throws IOException {
Resource resource = (Resource) templateSource;
return new InputStreamReader(resource.getInputStream());
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
}
});
successTemplate = cfg.getTemplate(successTemplatePath);
failTemplate = cfg.getTemplate(failTemplatePath);
infoTemplate = cfg.getTemplate(systemInformationTemplatePath);
reconnect();
}
@Override
public boolean reconnect() {
MailConfigurationDocument configuration = configurationMediator.getMailConfiguration();
session = getSession(configuration);
if (session == null) {
LOG.info("Disconnected from SMTP server");
return false;
} else {
LOG.info("Connected to SMTP server");
return true;
}
}
@Override
public void disconnect() {
LOG.info("Disconnected from SMTP server");
session = null;
}
@Override
public void notifyAboutSuccess(TaskEntry taskEntry) {
if (session != null && configurationMediator.getMailConfiguration().getEvents().isSuccess()) {
Set<String> recipients = configurationMediator.getMailConfiguration().getRecipients();
if (recipients != null && !recipients.isEmpty()) {
Map<String, Object> data = new HashMap<>();
data.put("domain", configurationMediator.getDomain());
data.put("task", taskEntry);
try {
notifyViaEmail(data, successSubject, successTemplate, recipients);
} catch (EmailNotificationException e) {
LOG.error(e);
}
}
}
}
@Override
public void notifyAboutError(TaskEntry taskEntry, Exception e) {
if (session != null && configurationMediator.getMailConfiguration().getEvents().isError()) {
Set<String> recipients = configurationMediator.getMailConfiguration().getRecipients();
if (recipients != null && !recipients.isEmpty()) {
Map<String, Object> data = new HashMap<>();
data.put("domain", configurationMediator.getDomain());
data.put("task", taskEntry);
data.put("errorMessage", e.getLocalizedMessage());
try {
notifyViaEmail(data, errorSubject, failTemplate, recipients);
} catch (EmailNotificationException ex) {
LOG.error(e);
}
}
}
}
@Override
public void notifyAboutSystemStatus(String message) {
if (session != null && configurationMediator.getMailConfiguration().getEvents().isInfo()) {
Set<String> recipients = configurationMediator.getMailConfiguration().getRecipients();
if (recipients != null && !recipients.isEmpty()) {
Map<String, String> data = new HashMap<>();
data.put("domain", configurationMediator.getDomain());
data.put("message", message);
try {
notifyViaEmail(data, infoSubject, infoTemplate, recipients);
} catch (EmailNotificationException e) {
LOG.error(e);
}
}
}
}
@Override
public void testConfiguration(MailConfigurationDto config, String testEmail, String domain) {
try {
MailConfigurationDocument document = new MailConfigurationDocument();
BeanUtils.copyProperties(config, document);
Session session = getSession(document, false);
Map<String, String> data = new HashMap<>();
data.put("domain", domain);
data.put("message", testMessage);
Set<String> recipients = new HashSet<>();
recipients.add(testEmail);
notifyViaEmail(data, testSubject, infoTemplate, recipients, session, config.getFromMailAddress());
} catch (Exception e) {
throw new ConfigurationException("Invalid configuration", e);
}
}
private void notifyViaEmail(Map data, String subject, Template template, Set<String> recipients) {
notifyViaEmail(data, subject, template, recipients, session, configurationMediator.getMailConfiguration().getFromMailAddress());
LOG.info("Email to {} was successfully sent", recipients);
}
private void notifyViaEmail(Map data, String subject, Template template, Set<String> recipients, Session session, String senderEmail) {
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(String.join(",", recipients)));
message.setSubject(subject);
StringWriter stringWriter = new StringWriter();
template.process(data, stringWriter);
message.setContent(stringWriter.toString(), "text/html; charset=utf-8");
Transport.send(message);
} catch (Exception e) {
LOG.error(e);
throw new EmailNotificationException(e);
}
}
private Session getSession(MailConfigurationDocument configuration) {
return getSession(configuration, true);
}
private Session getSession(MailConfigurationDocument configuration, boolean decryptPassword) {
if (configuration == null) {
return null;
}
try {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", configuration.getMailSMTPHost());
props.put("mail.smtp.port", configuration.getMailSMTPPort());
switch (configuration.getConnectionType()) {
case TLS:
props.put("mail.smtp.starttls.enable", "true");
break;
case SSL:
props.put("mail.smtp.socketFactory.port", configuration.getMailSMTPPort());
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
break;
}
return Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
if (decryptPassword) {
return new PasswordAuthentication(configuration.getUserName(),
cryptoService.decrypt(configurationMediator.getConfigurationId(), configuration.getPassword()));
} else {
return new PasswordAuthentication(configuration.getUserName(), configuration.getPassword());
}
}
});
} catch (RuntimeException e) {
LOG.error(e);
session = null;
return null;
}
}
}