How to create a mail merge template with Java

How to create a mail merge template with Java

10 min read
Tags:

Mail merge consists of using a template to send similar emails to multiple recipients while keeping the content personalized.

And why is this important? Well, you can simply copy and paste and replace the content and then send the emails, but what happens when you have 300 recipients? Copy and paste don’t seem so fun anymore. For high-volume sends, using a mail merge template is the way to go.

In this post we will use Gmail as our email service provider to create a mail merge template with Java. With the Nylas Email API, we can easily support utilizing a mail merge template to send communications. Let’s explore how in this blog post.

Is your system ready?

If you already have the Nylas Java SDK installed and your Java environment is configured, then continue along with the blog.

Otherwise, I would recommend that you read the post How to Send Emails with the Nylas Java SDK where the basic setup is clearly explained.

What are we going to talk about?

What our mail merge template will look like

Before we jump into the code, let’s see how our application actually works. Although for that, we need an essential step, which is creating a .csv file containing all the emails and information that we’re going to use.

The file will look like this, and you can actually choose how you name it:

NameLast_NameAccountAddressEmailGift
AlvaroTejada Galindo12345742 Evergreen Terrace[email protected]Deadpool Action Figure
BlagTejada Galindo56789430 Spalding Way[email protected]AFI Poster
Alvaro aka “Blag”Tejada Galindo464511024 Cherry Street[email protected]Tomb Raider Game

Keep in mind that you can add whatever you like here or you can even delete columns. The only ones that are “enforced” are Name and Email.

This is how our application will look like:

Mail Merge Template application

Here, we can fill in the subject, and the body and select the .csv file that we had created. 

We will get an error message if we don’t specify all the fields.

You must specify all fields

The exciting part here is how we define the subject and the body.

Filling out our Mail Merge Template

Here, {Name}, {Account}, {Address} and {Gift} will be replaced by the values stored on the .csv file. So each recipient will get a personalized email. Just keep in mind that the values used between brackets need to match the names in the .csv file.

Let’s click Submit and see what happens:

Emails sent confirmation

We have successfully sent emails to all recipients. Let’s check their inbox.

User 1 receive the email
User 2 receive the email
User 3 receive the email

As we can see, all recipients received a personalized email, as all the fields were replaced accordingly.

Creating the project

Our project is going to be called Mail_Merge, and it will have a main class called MailMerge. We will need to change the default pom.xml file and create two Mustache templates called main and show_emails.

We want to use a web framework, and a very light and fast option is SparkJava

Creating the pom.xml file

We’re going to include some useful libraries:

  • spark-core → The Spark Web Framework
  • nylas-java-sdk → The Nylas Java SDK
  • dotenv-java → To enable using .env files
  • spark-template-mustache → To allow mustache on Spark projects
  • opencsv → To deal with .csv files
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>Mail_Merge</groupId>
   <artifactId>Mail_Merge</artifactId>
   <version>1.0-SNAPSHOT</version>

   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <maven.compiler.source>17</maven.compiler.source>
       <maven.compiler.target>17</maven.compiler.target>
   </properties>

   <dependencies>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
           <version>1.7.25</version>
       </dependency>
       <dependency>
           <groupId>com.sparkjava</groupId>
           <artifactId>spark-core</artifactId>
           <version>2.9.4</version>
       </dependency>
       <dependency>
           <groupId>com.nylas.sdk</groupId>
           <artifactId>nylas-java-sdk</artifactId>
           <version>1.16.0</version>
       </dependency>
       <dependency>
           <groupId>io.github.cdimascio</groupId>
           <artifactId>dotenv-java</artifactId>
           <version>2.2.4</version>
       </dependency>
       <dependency>
           <groupId>com.sparkjava</groupId>
           <artifactId>spark-template-mustache</artifactId>
           <version>2.7.1</version>
       </dependency>
       <dependency>
           <groupId>com.opencsv</groupId>
           <artifactId>opencsv</artifactId>
           <version>5.5</version>
       </dependency>
   </dependencies>

</project>

And here’s the source code of our MailMerge.java class:

// Import Java Utilities
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.lang.Exception;

// Import Spark and Mustache libraries
import spark.ModelAndView;
import static spark.Spark.*;
import spark.template.mustache.MustacheTemplateEngine;

// Import Opencsv
import com.opencsv.CSVReader;

//Import Nylas Packages
import com.nylas.NylasAccount;
import com.nylas.NylasClient;
import com.nylas.Draft;
import com.nylas.NameEmail;

//Import DotEnv to handle .env files
import io.github.cdimascio.dotenv.Dotenv;

public class MailMerge {
   public static void main(String[] args) {
       // Load the .env file
       Dotenv dotenv = Dotenv.load();
       // Create the client object
       NylasClient client = new NylasClient();
       // Connect it to Nylas using the Access Token from the .env file
       NylasAccount account = client.account(dotenv.get("ACCESS_TOKEN"));

       // Default path when we load our web application
       get("/", (request, response) -> {
           // Create a model to pass information to the mustache template
           Map<String, Object> model = new HashMap<>();
           model.put("subject", "");
           model.put("body", "");
           // Call the mustache template
           return new ModelAndView(model, "main.mustache");
       }, new MustacheTemplateEngine());

       // When we submit the form, we're posting data
       post("/", (request, response) -> {
           // Grab the form fields
           String subject = request.queryParams("subject");
           String body = request.queryParams("body");
           String merge_file = request.queryParams("merge_file");
           // Validate that they are all filled
           // Otherwise halt execution and display an error message
           if(subject.equals("") || body.equals("") || merge_file.equals("")){
               String halt_msg = "<html>\n" +
                       "<head>\n" +
                       "    <script src=\"https://cdn.tailwindcss.com\"></script>\n" +
                       "    <title>Nylas' Mail Merge</title>\n" +
                       "</head>\n" +
                       "<body>\n" +
                       "<div class=\"bg-red-300 border-green-600 border-b p-4 m-4 rounded w-2/5 grid place-items-center\">\n" +
                       "<p class=\"font-semibold\">You must specify all fields</p>\n" +
                       "</div>\n" +
                       "</body>\n" +
                       "</html>";
               halt(halt_msg);
           }
           // Auxiliary variables
           Integer name = -1;
           Integer last_name = -1;
           Integer email = -1;
           String full_name = "";
           String email_address = "";
           String [] emails = {};
           // Create an Array List of emails
           ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(emails));
           // Read the .csv fil;e
           CSVReader reader = new CSVReader(new FileReader(merge_file));
           // Read the header or the column names
           String[] col_names = reader.readNext();
           String [] nextLine;
           // Read each line of the .csv file
           while ((nextLine = reader.readNext()) != null) {
               // Use extra variables to preserve original values
               String subject_replaced = subject;
               String body_replaced = body;
               // Loop through all the column names
               for(int i=0;i<col_names.length;i++){
                   if(col_names[i].equals("Name")){
                       name = i;
                   }
                   if(col_names[i].equals("Last_Name")){
                       last_name = i;
                   }
                   if(col_names[i].equals("Email")){
                       email = i;
                   }
                   // Use a small regex to determine what needs to be
                   // replaced. {Name} with the actual name on the .csv file
                   String pattern_key = "\\{" + col_names[i] + "\\}";
                   Pattern pattern = Pattern.compile(pattern_key, Pattern.CASE_INSENSITIVE);
                   Matcher matcher = pattern.matcher(subject);
                   boolean matchFound = matcher.find();
                   if(matchFound) {
                       subject_replaced = subject_replaced.replaceAll(pattern_key, nextLine[i]);
                   }
                   matcher = pattern.matcher(body);
                   matchFound = matcher.find();
                   if(matchFound) {
                       body_replaced = body_replaced.replaceAll(pattern_key, nextLine[i]);
                   }
               }
               // Get name or full name of recipient
               try{
                   full_name = nextLine[name] + " " + nextLine[last_name];
               }catch(Exception e){
                   full_name = nextLine[name];
               }
               // Get a list of recipient emails
               arrayList.add(nextLine[email]);
               email_address = nextLine[email];
               // Create a new draft
               Draft draft = new Draft();
               // Set the subject of the message
               draft.setSubject(subject_replaced);
               // Set the body of the message
               draft.setBody(body_replaced);
               // Specify the recipient of the email
               draft.setTo(Arrays.asList(new NameEmail(full_name, email_address)));
               try {
                   //Send the email draft
                   account.drafts().send(draft);
               }catch (Exception e){
               }
           }
           // Convert the ArrayList back to an Array
           emails = arrayList.toArray(emails);
           // Pass the emails as a parameter to the mustache template
           Map<String, Object> model = new HashMap<>();
           model.put("emails", emails);
           return new ModelAndView(model, "show_emails.mustache");
       }, new MustacheTemplateEngine());
   }
}

Inside the resources folder, we need to create a templates folder and inside we need to create 2 different files, let’s start with main.mustache:

<html>
<head>
   <script src="https://cdn.tailwindcss.com"></script>
   <title>Nylas' Mail Merge</title>
</head>
<body>
<div class="bg-green-300 border-green-600 border-b p-4 m-4 rounded w-2/5 grid place-items-center">
   <p class="text-6xl text-center">Mail Merge</p>
   <br>
   <form method="post">
       <label for="subject" class="font-bold">Subject: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
       <input type="text" name="subject" value="{{subject}}" size="50"></input>
       <br><br>
       <label for="body" class="font-bold">Body:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
       <textarea name="body" rows=5 cols=49>{{body}}</textarea>
       <br><br>
       <label for="merge_file" class="font-bold">Merge File: &nbsp;</label>
       <input type="file" name="merge_file">
       <br><br>
       <button type="submit" class="block bg-blue-500 hover:bg-blue-700 text-white text-lg mx-auto py-2 px-4 rounded-full">Submit</button>
   </form>
</div>
</body>
</html>

Then, show_emails.mustache:

<html>
<head>
   <script src="https://cdn.tailwindcss.com"></script>
   <title>Nylas' Mail Merge</title>
</head>
<body>
<div class="bg-green-300 border-green-600 border-b p-4 m-4 rounded w-2/5 grid place-items-center">
   <h1 class="text-3xl"> The email was sent to the following addresses</h1>
   <br>
   {{#emails}}
       <p class="font-semibold">{{.}}</p>
   {{/emails}}
</div>

<div class="bg-green-300 border-green-600 border-b p-4 m-4 rounded w-2/5 grid place-items-center">
   <a href="/" class="text-blue-600">Go back</a>
</div>
</body>
</html>

And that’s it. We’re ready to roll.

Running our mail merge template

In order to run our application, we need to compile it by typing the following on the terminal window:

$ mvn package
Compiliing our Mail Merge Template application

To actually run the application, we need to type the following:

$ mvn exec:java -Dexec.mainClass="MailMerge"
Running our Mail Merge Template application

Our application will be running on port 4567 of localhost, so we just need to open our favourite browser and go to the following address:

http://localhost:4567

We easily created a mail merge template application using Java.

If you want to learn more about the Nylas Email API, please visit our documentation Email API Overview.

Sign up for your Nylas account and start building for free.

Related resources

How to build a CRM in 3 sprints with Nylas

What is a CRM? CRM stands for Customer Relationship Management, and it’s basically a way…

How to create an appointment scheduler in your React app

Learn how to create an appointment scheduler in your React app using Nylas Scheduler. Streamline bookings and UX with step-by-step guidance.

Beyond APIs: Designing elegant, smart Web Components

Dive into the world of smart Web Components for advanced API integration solutions. Design elegant, customizable elements to elevate user experiences.