Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until end-of-day today:

>> GET ACCESS NOW

NPI – Lightrun – Spring (partner)

We rely on other people’s code in our own work. Every day. It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.

The problem is, of course, when things fall apart in production - debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky. It’s difficult to understand what talks to what and, specifically, which part of the underlying library is at fault.

Lightrun is a new kind of debugger.

It's one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics. No hotfixes, redeployments, or restarts required.

Learn more in this quick, 5-minute Lightrun tutorial:

>> The Essential List of Spring Boot Annotations and Their Use Cases

1. Overview

In our previous article, we saw how to use Spring to write and send text emails.

But it's also possible to use Spring template engines to write beautiful HTML emails with dynamic content.

In this tutorial, we're going to learn how to do it using the most famous of them: Thymeleaf and FreeMarker.

2. Spring HTML Emails

Let's start from the Spring Email tutorial.

First, we'll add a method to the EmailServiceImpl class to send emails with an HTML body:

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);
    emailSender.send(message);
}

We're using MimeMessageHelper to populate the message. The important part is the true value passed to the setText method: it specifies the HTML content type.

Let's see now how to build this htmlBody using Thymeleaf and FreeMarker templates.

3. Thymeleaf Configuration

Let's start with the configuration. We can isolate this in a class called EmailConfiguration.

First, we should provide a template resolver to locate the template files directory.

3.1. Templates as Classpath Resources

Template files can be shipped within the JAR file, which is the simplest way to maintain cohesion between templates and their input data.

To locate templates from the JAR, we use the ClassLoaderTemplateResolver. Our templates are in the main/resources/mail-templates directory, so we set the Prefix attribute relative to the resource directory:

@Bean
public ITemplateResolver thymeleafTemplateResolver() {
    ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
    templateResolver.setPrefix("mail-templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML");
    templateResolver.setCharacterEncoding("UTF-8");
    return templateResolver;
}

3.2. Templates From External Directory

In other cases, we may want to modify templates without having to rebuild and deploy. To achieve this, we can put the templates on the filesystem instead.

It might be useful to configure this path in application.properties so that we can modify it for each deployment. This property can be accessed using the @Value annotation:

@Value("${spring.mail.templates.path}")
private String mailTemplatesPath;

We then pass this value to a FileTemplateResolver, in place of the ClassLoaderTemplateResolver in our thymeleafTemplateResolver method:

FileTemplateResolver templateResolver = new FileTemplateResolver();
templateResolver.setPrefix(mailTemplatesPath);

3.3. Configure the Thymeleaf Engine

The final step is to create the factory method for the Thymeleaf engine. We'll need to tell the engine which TemplateResolver we've chosen, which we can inject via a parameter to the bean factory method:

@Bean
public SpringTemplateEngine thymeleafTemplateEngine(ITemplateResolver templateResolver) {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    templateEngine.setTemplateEngineMessageSource(emailMessageSource());
    return templateEngine;
}

Here, the resolver we created earlier is injected automatically by Spring into the template engine factory method.

4. FreeMarker Configuration

In the same fashion as Thymeleaf, in the EmailConfiguration class, we'll configure the template resolver for FreeMarker templates (.ftl):
And this time, the location of the templates will be configured in the FreeMarkerConfigurer bean.

4.1. Templates in the Classpath

Here, we have the same options as for Thymeleaf. Let's configure templates as classpath resources:

@Bean 
public FreeMarkerConfigurer freemarkerClassLoaderConfig() {
    Configuration configuration = new Configuration(Configuration.VERSION_2_3_27);
    TemplateLoader templateLoader = new ClassTemplateLoader(this.getClass(), "/mail-templates");
    configuration.setTemplateLoader(templateLoader);
    FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
    freeMarkerConfigurer.setConfiguration(configuration);
    return freeMarkerConfigurer; 
}

4.2. Templates on the File System

To configure templates from another path in the filesystem, we'll need to replace the TemplateLoader instance:

TemplateLoader templateLoader = new FileTemplateLoader(new File(mailTemplatesPath));

5. Localization with Thymeleaf and FreeMarker

In order to manage translations with Thymeleaf, we can specify a MessageSource instance to the engine:

@Bean
public ResourceBundleMessageSource emailMessageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("mailMessages");
    return messageSource;
}
@Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
   ...
   templateEngine.setTemplateEngineMessageSource(emailMessageSource());
   ...
}

Then, we'd create resource bundles for each locale we support:

src/main/resources/mailMessages_xx_YY.properties

As FreeMarker proposes localization by duplicating the templates, we don't have to configure the message source there.

6. Thymeleaf Templates Content

Next, let's have a look at the template-thymeleaf.html file:

<!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 />
    </p>
  </body>
</html>

As can be seen, we've used Thymeleaf notation, that is, ${…} for variables and #{…} for localized strings.

As the template engine is correctly configured, it's very simple to use it: We'll just create a Context object that contains template variables (passed as a Map here).

Then, we'll pass it to the process method along with the template name:

@Autowired
private SpringTemplateEngine thymeleafTemplateEngine;

@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);
}

Now, let's see how to do the same thing with FreeMarker.

7. FreeMarker Templates Content

As can be seen, FreeMarker's syntax is more simple, but again it does not manage localized strings. So, here's the English version:

<!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 />
      </p>
    </body>
</html>

Then, we should use the FreeMarkerConfigurer class to get the template file, and finally, FreeMarkerTemplateUtils to inject data from our Map:

@Autowired
private FreeMarkerConfigurer freemarkerConfigurer;

@Override
public void sendMessageUsingFreemarkerTemplate(
    String to, String subject, Map<String, Object> templateModel)
        throws IOException, TemplateException, MessagingException {
        
    Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
      .getTemplate("template-freemarker.ftl");
    String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);

    sendHtmlMessage(to, subject, htmlBody);
}

To go further, we'll see how to add a logo to our email signature.

8. Emails With Embedded Images

Since it's very common to include images in an HTML email, we'll see how to do this using a CID attachment.

The first change concerns the sendHtmlMessage method. We have to set MimeMessageHelper as multi-part by passing true to the second argument of the constructor:

MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

Then, we have to get the image file as a resource. We can use the @Value annotation for this:

@Value("classpath:/mail-logo.png")
Resource resourceFile;

Notice that the mail-logo.png file is in the src/main/resources directory.

Back to the sendHtmlMessage method, we'll add resourceFile as an inline attachment, to be able to reference it with CID:

helper.addInline("attachment.png", resourceFile);

Finally, the image has to be referenced from both Thymeleaf and FreeMarker emails using CID notation:

<img src="cid:attachment.png" />

9. Conclusion

In this article, we've seen how to send Thymeleaf and FreeMarker emails, including rich HTML content.

To conclude, most of the work is related to Spring; therefore, the use of one or the other is quite similar for a simple need such as sending emails.

As always, the full source code of the examples can be found over on GitHub.

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until end-of-day today:

>> GET ACCESS NOW

Generic footer banner
Comments are closed on this article!