BAEL-3964 HTML emails with Thymeleaf and FreeMarker (#9119)

This commit is contained in:
Benjamin Caure 2020-04-23 07:23:11 +02:00 committed by GitHub
parent c7b980c133
commit 8bd69814b0
17 changed files with 396 additions and 65 deletions

View File

@ -9,6 +9,7 @@ This module contains articles about Spring MVC
- [Servlet Redirect vs Forward](https://www.baeldung.com/servlet-redirect-forward)
- [Apache Tiles Integration with Spring MVC](https://www.baeldung.com/spring-mvc-apache-tiles)
- [Guide to Spring Email](https://www.baeldung.com/spring-email)
- [Using ThymeLeaf and FreeMarker Emails Templates with Spring](https://www.baeldung.com/thymeleaf-freemarker-email)
- [Request Method Not Supported (405) in Spring](https://www.baeldung.com/spring-request-method-not-supported-405)
- [Spring @RequestParam Annotation](https://www.baeldung.com/spring-request-param)
- More articles: [[more -->]](/spring-mvc-basics-3)

View File

@ -2,7 +2,6 @@ package com.baeldung.spring.configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@ -10,9 +9,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@ -65,29 +61,5 @@ public class ApplicationConfiguration implements WebMvcConfigurer {
converters.add(new RssChannelHttpMessageConverter());
converters.add(new JsonChannelHttpMessageConverter());
}
@Bean
public SimpleMailMessage templateSimpleMessage() {
SimpleMailMessage message = new SimpleMailMessage();
message.setText("This is the test email template for your email:\n%s\n");
return message;
}
@Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("my.gmail@gmail.com");
mailSender.setPassword("password");
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.debug", "true");
return mailSender;
}
}

View File

@ -0,0 +1,89 @@
package com.baeldung.spring.configuration;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
@Configuration
@ComponentScan(basePackages = { "com.baeldung.spring.mail" })
public class EmailConfiguration {
@Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("my.gmail@gmail.com");
mailSender.setPassword("password");
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "false");
props.put("mail.debug", "true");
return mailSender;
}
@Bean
public SimpleMailMessage templateSimpleMessage() {
SimpleMailMessage message = new SimpleMailMessage();
message.setText("This is the test email template for your email:\n%s\n");
return message;
}
@Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
templateEngine.setTemplateEngineMessageSource(emailMessageSource());
return templateEngine;
}
@Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("/WEB-INF/views/mail/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
@Bean
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/views/mail");
return freeMarkerConfigurer;
}
@Bean
public FreeMarkerViewResolver freemarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache(true);
resolver.setPrefix("");
resolver.setSuffix(".ftl");
return resolver;
}
@Bean
public ResourceBundleMessageSource emailMessageSource() {
final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("/mailMessages");
return messageSource;
}
}

View File

@ -20,6 +20,7 @@ public class WebInitializer implements WebApplicationInitializer {
// ctx.register(GroovyConfiguration.class);
// ctx.register(JadeTemplateConfiguration.class);
// ctx.register(PushConfiguration.class);
ctx.register(EmailConfiguration.class);
// ctx.setServletContext(container);
//ctx.register(TilesApplicationConfiguration.class);

View File

@ -1,10 +1,17 @@
package com.baeldung.spring.controller;
import com.baeldung.spring.mail.EmailServiceImpl;
import com.baeldung.spring.domain.MailObject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
@ -12,26 +19,21 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.baeldung.spring.domain.MailObject;
import com.baeldung.spring.mail.EmailService;
import freemarker.template.TemplateException;
@Controller
@RequestMapping("/mail")
public class MailController {
@Autowired
public EmailServiceImpl emailService;
public EmailService emailService;
@Value("${attachment.invoice}")
private String attachmentPath;
@Autowired
public SimpleMailMessage template;
private static final Map<String, Map<String, String>> labels;
static {
@ -46,7 +48,7 @@ public class MailController {
//Email with template
props = new HashMap<>();
props.put("headerText", "Send Email Using Template");
props.put("headerText", "Send Email Using Text Template");
props.put("messageLabel", "Template Parameter");
props.put("additionalInfo",
"The parameter value will be added to the following message template:<br>" +
@ -60,6 +62,7 @@ public class MailController {
props.put("messageLabel", "Message");
props.put("additionalInfo", "To make sure that you send an attachment with this email, change the value for the 'attachment.invoice' in the application.properties file to the path to the attachment.");
labels.put("sendAttachment", props);
}
@RequestMapping(method = RequestMethod.GET)
@ -85,6 +88,7 @@ public class MailController {
return "mail/send";
}
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String createMail(Model model,
@ModelAttribute("mailObject") @Valid MailObject mailObject,
@ -95,7 +99,7 @@ public class MailController {
emailService.sendSimpleMessage(mailObject.getTo(),
mailObject.getSubject(), mailObject.getText());
return "redirect:/home";
return "emails";
}
@RequestMapping(value = "/sendTemplate", method = RequestMethod.POST)
@ -107,10 +111,9 @@ public class MailController {
}
emailService.sendSimpleMessageUsingTemplate(mailObject.getTo(),
mailObject.getSubject(),
template,
mailObject.getText());
return "redirect:/home";
return "redirect:/mail";
}
@RequestMapping(value = "/sendAttachment", method = RequestMethod.POST)
@ -127,6 +130,47 @@ public class MailController {
attachmentPath
);
return "redirect:/home";
return "redirect:/mail";
}
@RequestMapping(value = {"/sendHtml"}, method = RequestMethod.GET)
public String getHtmlMailView(Model model,
HttpServletRequest request) {
Map<String, String> templateEngines = new HashMap<>();
templateEngines.put("Thymeleaf", "Thymeleaf");
templateEngines.put("Freemarker", "Freemarker");
model.addAttribute("mailObject", new MailObject());
model.addAttribute("templateEngines", templateEngines);
return "mail/sendHtml";
}
@RequestMapping(value = "/sendHtml", method = RequestMethod.POST)
public String createHtmlMail(Model model,
@ModelAttribute("mailObject") @Valid MailObject mailObject,
Errors errors) throws IOException, MessagingException, TemplateException {
if (errors.hasErrors()) {
return "mail/send";
}
Map<String, Object> templateModel = new HashMap<>();
templateModel.put("recipientName", mailObject.getRecipientName());
templateModel.put("text", mailObject.getText());
templateModel.put("senderName", mailObject.getSenderName());
if (mailObject.getTemplateEngine().equalsIgnoreCase("thymeleaf")) {
emailService.sendMessageUsingThymeleafTemplate(
mailObject.getTo(),
mailObject.getSubject(),
templateModel);
} else {
emailService.sendMessageUsingFreemarkerTemplate(
mailObject.getTo(),
mailObject.getSubject(),
templateModel);
}
return "redirect:/mail";
}
}

View File

@ -12,8 +12,11 @@ public class MailObject {
@NotNull
@Size(min = 1, message = "Please, set an email address to send the message to it")
private String to;
private String recipientName;
private String subject;
private String text;
private String senderName;
private String templateEngine;
public String getTo() {
return to;
@ -38,4 +41,30 @@ public class MailObject {
public void setText(String text) {
this.text = text;
}
public String getRecipientName() {
return recipientName;
}
public void setRecipientName(String recipientName) {
this.recipientName = recipientName;
}
public String getSenderName() {
return senderName;
}
public void setSenderName(String senderName) {
this.senderName = senderName;
}
public String getTemplateEngine() {
return templateEngine;
}
public void setTemplateEngine(String templateEngine) {
this.templateEngine = templateEngine;
}
}

View File

@ -1,6 +1,11 @@
package com.baeldung.spring.mail;
import org.springframework.mail.SimpleMailMessage;
import java.io.IOException;
import java.util.Map;
import javax.mail.MessagingException;
import freemarker.template.TemplateException;
/**
* Created by Olga on 8/22/2016.
@ -11,10 +16,19 @@ public interface EmailService {
String text);
void sendSimpleMessageUsingTemplate(String to,
String subject,
SimpleMailMessage template,
String ...templateArgs);
String ...templateModel);
void sendMessageWithAttachment(String to,
String subject,
String text,
String pathToAttachment);
void sendMessageUsingThymeleafTemplate(String to,
String subject,
Map<String, Object> templateModel)
throws IOException, MessagingException;
void sendMessageUsingFreemarkerTemplate(String to,
String subject,
Map<String, Object> templateModel)
throws IOException, TemplateException, MessagingException;
}

View File

@ -1,26 +1,49 @@
package com.baeldung.spring.mail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* Created by Olga on 7/15/2016.
*/
@Component
@Service("EmailService")
public class EmailServiceImpl implements EmailService {
@Autowired
public JavaMailSender emailSender;
@Autowired
public SimpleMailMessage template;
@Autowired
private SpringTemplateEngine thymeleafTemplateEngine;
@Autowired
private FreeMarkerConfigurer freemarkerConfigurer;
@Value("classpath:/mail-logo.png")
Resource resourceFile;
public void sendSimpleMessage(String to, String subject, String text) {
try {
@ -38,9 +61,8 @@ public class EmailServiceImpl implements EmailService {
@Override
public void sendSimpleMessageUsingTemplate(String to,
String subject,
SimpleMailMessage template,
String ...templateArgs) {
String text = String.format(template.getText(), templateArgs);
String ...templateModel) {
String text = String.format(template.getText(), templateModel);
sendSimpleMessage(to, subject, text);
}
@ -66,4 +88,42 @@ public class EmailServiceImpl implements EmailService {
e.printStackTrace();
}
}
@Override
public void sendMessageUsingThymeleafTemplate(
String to, String subject, Map<String, Object> templateModel)
throws MessagingException {
Context thymeleafContext = new Context();
thymeleafContext.setVariables(templateModel);
String htmlBody = thymeleafTemplateEngine.process("template-thymeleaf.html", thymeleafContext);
sendHtmlMessage(to, subject, htmlBody);
}
@Override
public void sendMessageUsingFreemarkerTemplate(
String to, String subject, Map<String, Object> templateModel)
throws IOException, TemplateException, MessagingException {
Template freemarkerTemplate = freemarkerConfigurer.createConfiguration().getTemplate("template-freemarker.ftl");
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
sendHtmlMessage(to, subject, htmlBody);
}
private void sendHtmlMessage(String to, String subject, String htmlBody) throws MessagingException {
MimeMessage message = emailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlBody, true);
helper.addInline("attachment.png", resourceFile);
emailSender.send(message);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,5 @@
greetings=Hi {0},
subscriptions=Please find below your current subscriptions to our forum:
regards=Regards,
unsubscribe=Unsubscribe from these emails here
signature={0} at Baeldung

View File

@ -0,0 +1,5 @@
greetings=Bonjour {0},
subscriptions=Voici vos différentes souscriptions sur notre forum :
regards=Cordialement,
unsubscribe=Se désinscrire de ces emails ici
signature={0} à Baeldung

View File

@ -26,7 +26,7 @@
</tr>
<tr>
<td>
<input type="submit" formaction="mail/sendTemplate" value="Send Email Using Template">
<input type="submit" formaction="mail/sendTemplate" value="Send Email Using Text Template">
</td>
</tr>
<tr>
@ -34,6 +34,11 @@
<input type="submit" formaction="mail/sendAttachment" value="Send Email With Attachment">
</td>
</tr>
<tr>
<td>
<input type="submit" formaction="mail/sendHtml" value="Send HTML Email">
</td>
</tr>
</table>
</fieldset>
</form>

View File

@ -0,0 +1,73 @@
<%--
User: Benjamin CAURE
Date: 4/14/2020
--%>
<%@ page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Send HTML Email</title>
</head>
<body>
<div>
<h3>Send Email Using Text Template</h3>
<form:form method="POST" modelAttribute="mailObject" >
<fieldset>
<div style="width: 100%;max-width: 1280px">
<table>
<tr>
<th><label for="input_to">Recipient email</label></th>
<td><form:input path="to" id="input_to" type="email"/>
<small>Enter email address</small><br/>
<form:errors path="to" cssStyle="color:red;font-size:small"/>
</td>
</tr>
<tr>
<th><label for="input_recipient_name">Recipient name</label></th>
<td><form:input path="recipientName" id="input_recipient_name"/>
<small>Enter the recipient name</small><br/>
<form:errors path="recipientName" cssStyle="color:red;font-size:small"/>
</td>
</tr>
<tr>
<th><label for="input_subject">Subject</label></th>
<td><form:input path="subject" id="input_subject"/>
<small>Enter the subject</small><br/>
<form:errors path="subject" cssStyle="color:red;font-size:small"/>
</td>
</tr>
<tr>
<th><label for="input_text">Text</label></th>
<td><form:textarea path="text"
rows="5" cols="50"
id="input_text"/>
<form:errors path="text" cssStyle="color:red;font-size:small"/>
</td>
</tr>
<tr>
<th><label for="input_sender_name">Sender name</label></th>
<td><form:input path="senderName" id="input_sender_name"/>
<small>Enter the sender name</small><br/>
<form:errors path="senderName" cssStyle="color:red;font-size:small"/>
</td>
</tr>
<tr>
<th><label for="input_template_engine">Template Engine</label></th>
<td><form:select path="templateEngine" id="input_template_engine" items="${templateEngines}"/>
<small>Select the template engine</small><br/>
<form:errors path="templateEngine" cssStyle="color:red;font-size:small"/>
</td>
</tr>
<tr>
<th></th>
<td>
<input type="submit" value="Send">
</td>
</tr>
</table>
</div>
</fieldset>
</form:form>
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Hi ${recipientName}</p>
<p>${text}</p>
<p>Regards,</p>
<p>
<em>${senderName} at Baeldung</em> <br />
<img src="cid:attachment.png" />
</p>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{greetings(${recipientName})}"></p>
<p th:text="${text}"></p>
<p th:text="#{regards}"></p>
<p>
<em th:text="#{signature(${senderName})}"></em> <br />
<img src="cid:attachment.png" />
</p>
</body>
</html>

View File

@ -7,10 +7,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.baeldung.spring.configuration.ApplicationConfiguration;
import com.baeldung.spring.configuration.EmailConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={ApplicationConfiguration.class})
@ContextConfiguration(classes={ApplicationConfiguration.class, EmailConfiguration.class})
@WebAppConfiguration
public class SpringContextTest {

View File

@ -1,6 +1,8 @@
package com.baeldung.controller.rss;
import com.baeldung.spring.configuration.ApplicationConfiguration;
import com.baeldung.spring.configuration.EmailConfiguration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -13,7 +15,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringJUnitWebConfig(ApplicationConfiguration.class)
@SpringJUnitWebConfig(classes={ApplicationConfiguration.class, EmailConfiguration.class})
public class ArticleRssIntegrationTest {
public static final String APPLICATION_RSS_XML = "application/rss+xml";
public static final String APPLICATION_RSS_JSON = "application/rss+json";