package fr.openwide.core.spring.notification.service.impl; import static com.google.common.base.Preconditions.checkNotNull; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.javatuples.LabelValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import org.springframework.util.Assert; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import fr.openwide.core.spring.notification.exception.NotificationContentRenderingException; import fr.openwide.core.spring.notification.model.INotificationContentDescriptor; import fr.openwide.core.spring.notification.model.INotificationRecipient; import fr.openwide.core.spring.property.service.IPropertyService; import freemarker.template.Configuration; import freemarker.template.TemplateException; public class FreemarkerTemplateNotificationContentDescriptorImpl implements INotificationContentDescriptor { private static final Logger LOGGER = LoggerFactory.getLogger(FreemarkerTemplateNotificationContentDescriptorImpl.class); private static final String ATTACHMENT_NAMES_VARIABLE_NAME = "attachments"; private final Configuration templateConfiguration; private final String templateKey; private final Map<Locale, HashMap<String, Object>> templateVariablesByLocale = new HashMap<Locale, HashMap<String,Object>>(); private final Collection<LabelValue<String, File>> attachments; private final Locale defaultLocale; @Autowired private IPropertyService propertyService; public FreemarkerTemplateNotificationContentDescriptorImpl( Configuration templateConfiguration, String templateKey, Collection<LabelValue<String, File>> attachments, Locale defaultLocale) { super(); this.templateConfiguration = checkNotNull(templateConfiguration); this.templateKey = checkNotNull(templateKey); this.attachments = checkNotNull(attachments); this.defaultLocale = defaultLocale; } @Override public String renderSubject() throws NotificationContentRenderingException { return renderSubject(defaultLocale); } private String renderSubject(Locale locale) throws NotificationContentRenderingException { try { return getMailElement(MailElement.SUBJECT, locale); } catch (TemplateException | IOException e) { throw new NotificationContentRenderingException("Exception while rendering notification subject from freemarker template", e); } } @Override public String renderHtmlBody() { return null; } @Override public String renderTextBody() throws NotificationContentRenderingException { return renderTextBody(defaultLocale); } private String renderTextBody(Locale locale) throws NotificationContentRenderingException { try { return getMailElement(MailElement.BODY_TEXT, locale); } catch (TemplateException | IOException e) { throw new NotificationContentRenderingException("Exception while rendering notification body from freemarker template", e); } } @Override public INotificationContentDescriptor withContext(INotificationRecipient recipient) { Locale recipientLocale = propertyService.toAvailableLocale(recipient.getLocale()); return new Wrapper(this, recipientLocale); } private static class Wrapper implements INotificationContentDescriptor { private final FreemarkerTemplateNotificationContentDescriptorImpl wrapped; private final Locale locale; public Wrapper(FreemarkerTemplateNotificationContentDescriptorImpl wrapped, Locale locale) { super(); this.wrapped = wrapped; this.locale = locale; } @Override public boolean equals(Object obj) { if (obj instanceof Wrapper) { if (obj == this) { return true; } Wrapper other = (Wrapper) obj; return new EqualsBuilder() .append(wrapped, other.wrapped) .append(locale, other.locale) .build(); } return false; } @Override public int hashCode() { return new HashCodeBuilder() .append(wrapped) .append(locale) .build(); } @Override public String renderSubject() throws NotificationContentRenderingException { return wrapped.renderSubject(locale); } @Override public String renderHtmlBody() throws NotificationContentRenderingException { return wrapped.renderHtmlBody(); } @Override public String renderTextBody() throws NotificationContentRenderingException { return wrapped.renderTextBody(locale); } @Override public INotificationContentDescriptor withContext(INotificationRecipient recipient) { return wrapped.withContext(recipient); } } /** * If locale == null, the variable will be considered as not locale-sensitive and will be available for all locales. */ public void setVariable(Locale locale, String name, Object value) { Assert.hasText(name, "Variable name must contain text"); if (!templateVariablesByLocale.containsKey(locale)) { templateVariablesByLocale.put(locale, new HashMap<String, Object>()); } templateVariablesByLocale.get(locale).put(name, value); } /** * If locale == null, the variables will be considered as not locale-sensitive and will be available for all locales. */ public void setVariables(Locale locale, Map<String, ?> variables) { if (!templateVariablesByLocale.containsKey(locale)) { templateVariablesByLocale.put(locale, new HashMap<String, Object>()); } templateVariablesByLocale.get(locale).putAll(variables); } private Map<String, Object> getTemplateVariables(Locale locale) { Map<String, Object> templateVariables = Maps.newHashMap(); Map<String, Object> sharedVariables = templateVariablesByLocale.get(null); if (sharedVariables != null) { templateVariables.putAll(sharedVariables); } Map<String, Object> localeDependentVariables = templateVariablesByLocale.get(locale); if (localeDependentVariables != null) { templateVariables.putAll(localeDependentVariables); } if (!attachments.isEmpty()) { if (!templateVariables.containsKey(ATTACHMENT_NAMES_VARIABLE_NAME)) { Collection<String> labels = Lists.newArrayList(); for (LabelValue<String, ?> labelValue : attachments) { labels.add(labelValue.getLabel()); } templateVariables.put(ATTACHMENT_NAMES_VARIABLE_NAME, labels); } else { LOGGER.warn(ATTACHMENT_NAMES_VARIABLE_NAME + " already present in the map. We don't override it."); } } return templateVariables; } private String getMailElement(MailElement element, Locale locale) throws IOException, TemplateException { Map<String, Object> freemarkerModelMap = getTemplateVariables(locale); if (freemarkerModelMap.containsKey(element.toString())) { throw new IllegalStateException(String.format("Variable name '%1$s' is reserved to implementation purposes. Users cannot use it.", element)); } freemarkerModelMap.put(element.toString(), true); return FreeMarkerTemplateUtils.processTemplateIntoString(templateConfiguration.getTemplate(templateKey, locale), freemarkerModelMap); } private enum MailElement { BODY_TEXT, SUBJECT } }