package io.kaif.mail;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.mail.MailException;
import org.springframework.mail.MailParseException;
import org.springframework.stereotype.Component;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import freemarker.template.Configuration;
import freemarker.template.SimpleScalar;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
@Component
public class MailComposer {
/**
* see http://stackoverflow.com/a/16572888 for how to manually use ResourceBundle in freemarker
* template
*/
public class FreemarkerMessageModel implements TemplateMethodModelEx {
private final MessageSource messageSource;
private final Locale locale;
public FreemarkerMessageModel(MessageSource messageSource, Locale locale) {
this.messageSource = messageSource;
this.locale = locale;
}
@Override
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() == 0) {
throw new TemplateModelException("Wrong number of arguments");
}
SimpleScalar simpleScalar = (SimpleScalar) arguments.get(0);
if (simpleScalar == null || Strings.isNullOrEmpty(simpleScalar.getAsString())) {
throw new TemplateModelException("Invalid code value '" + simpleScalar + "'");
}
@SuppressWarnings("unchecked")
Object[] args = arguments.stream().skip(1).map(Objects::toString).toArray(String[]::new);
return messageSource.getMessage(simpleScalar.getAsString(), args, locale);
}
}
@Autowired
private MessageSource messageSource;
@Autowired
private Configuration configuration;
@Autowired
private MailProperties mailProperties;
public MailComposer() {
//spring
}
@VisibleForTesting
MailComposer(MessageSource messageSource,
Configuration configuration,
MailProperties mailProperties) {
this.messageSource = messageSource;
this.configuration = configuration;
this.mailProperties = mailProperties;
}
public Mail createMail() {
Mail mail = new Mail();
mail.setFrom(mailProperties.getSenderAddress());
mail.setFromName(mailProperties.getSenderName());
return mail;
}
public String i18n(Locale locale, String key, Object... args) {
return messageSource.getMessage(key, args, locale);
}
public String compose(Locale locale, String templateFile, Map<String, Object> model)
throws MailException {
try {
Template template = configuration.getTemplate(templateFile, locale);
StringWriter writer = new StringWriter();
Map<String, Object> i18nModel = createI18nModel(locale);
i18nModel.putAll(model);
template.process(i18nModel, writer);
writer.flush();
return writer.toString();
} catch (IOException e) {
throw new MailParseException("unexpect IO error", e);
} catch (TemplateException e) {
throw new MailParseException("template error", e);
}
}
/**
* with FreemarkerMessageModel bind to <code>message</code>, the template can invoke method to
* get
* i18n:
* <pre>
* ${message('mail_activation_hint')}
* </pre>
*/
private Map<String, Object> createI18nModel(Locale locale) {
HashMap<String, Object> model = new HashMap<>();
model.put("message", new FreemarkerMessageModel(messageSource, locale));
return model;
}
}