Java application email libraries include: Java mail native, Spring, and apache email (and others). Lots of articles say apache email API is more intuitive, but others say if you are already using a Java/Spring application, then to go ahead and use the Spring one. I decided to try the Spring Boot one first, since we have a Java/Spring application.
The Spring boot library satisfied the three criteria below, and seems to work great on my local environment:
- the ability to send simple text email
- the ability to send HTML email (support for HTML emails already had the Thymeleaf HTML templating library provided with Spring Boot)
- the ability to attach a file(s) to the email
Environment:
Windows 7
Oracle Java 1.8.0_66
Spring Boot 1.3.1.RELEASE
Sprint Boot 1.3.1.RELEASE spring-boot-starter-thymeleaf
The example below supports criteria number 2 to send HTML emails using Thymeleaf email templates. I've also included unescaped HTML, passing multiple variables to HTML template, and inline image in the example below as well.
src/main/resources/templates/logo_cherryshoe.png: cherryshoe logo to put as inline image in HTML email
src/main/resources/application.properties: add applicable properties
# Email
spring.mail.host=<email smtp host>
spring.mail.port=<email smtp port>
spring.mail.defaultEncoding=UTF-8
spring.mail.properties.mail.smtp.connecttimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000
cherryshoe.mail.defaultFrom=<email>
# must match thyme html template name under templates resource folder
cherryshoe.mail.defaultMailTemplate=email
src/main/resources/templates/email.html: Thyme HTML Email template, used as the default email template
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head><title>CherryShoe Html Email Example</title>
<style>
body {
background-color: white;
}
/* White space is preserved with \n\n\n\n and css class a, span, tr { white-space: pre; } */
a, span, tr { white-space: pre; }
body {
color: black;
font-family: 'Arial', sans-serif;
font-weight: normal;
font-style: normal;
font-size: x-small;
}
table {
border-collapse: collapse;
width: 100%;
}
table, th, td {
border: 1px solid black;
}
.ten {
width: 10%;
}
.forty {
width: 40%;
}
.twenty-five {
width: 25%;
}
th {
background-color: #C6DEFF;
color: black;
vertical-align: middle;
}
th, td {
padding: 1px;
text-align: left;
vertical-align: middle;
}
.cherryshoe-font-bold {
color: black;
font-family: 'Arial', sans-serif;
font-weight: bold;
font-style: normal;
font-size: x-small;
}
</style>
</head>
<body>
Hi <span th:text="${name}"></span>, <br></br><br></br>
<!-- Need br formatted this way for SAX Parser to parse with no errors -->
<br></br><br></br>
<span class="cherryshoe-font-bold">Example Table Data generated from backend service</span>
<table>
<tr>
<th class="twenty-five">Column 1</th>
<th class="ten">Column 2</th>
<th class="twenty-five">Column 3</th>
<th class="forty">Column 4</th>
</tr>
<!-- Use th:utext to show unescaped html -->
<span th:utext="${table1}"></span>
</table>
<br></br><br></br>
Thank you
<br></br>
<img src="logo_cherryshoe.png" th:src="'cid:' + ${imageResourceName}" alt="CherryShoe Logo"/>
</body>
</html>
src/main/java/com/cherryshoe.service.email.SendEmailService.java
package com.cherryshoe.service.email;
import java.io.IOException;
public interface SendEmailService {
/**
* Send html emails.
*
* @throws IOException
*/
public void sendHtmlEmail() throws IOException;
}
src/main/java/com/cherryshoe.service.email.SendEmailServiceSpringBootImpl
package com.cherryshoe.service.email;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
@Service
/*
* White space is preserved in text email with \n. It is also preserved in HTML
* email with the css in the html template
*/
public class SendEmailServiceSpringBootImpl implements SendEmailService {
// JavaMailSenderImpl is thread safe after it's constructed
@Autowired
private JavaMailSender mailSender;
// needed for HTML email templating
@Autowired
private TemplateEngine templateEngine;
@Value("${cherryshoe.mail.defaultFrom}")
private String defaultFrom;
// The default configuration of Thymeleaf expects that all HTML files are
// placed under resources/templates directory and ends with the .html
// extension.
// Can also externalize the templates/*.html files
@Value("${cherryshoe.mail.defaultMailTemplate}")
private String defaultMailTemplate;
private Logger log = LoggerFactory.getLogger(getClass());
private final ThreadLocal<DateFormat> threadLocalDf = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("MM/dd/yyyy");
}
};
@Override
public void sendHtmlEmail() throws IOException {
MimeMessage mail = mailSender.createMimeMessage();
InputStream imageIs = null;
try {
// set multiple context variables key/value pairs for email template
String templateMailBodyNameKey = "name";
String templateMailBodyNameVal = "[Name]";
String templateMailBodyTable1Key = "table1";
StringBuffer templateMailBodyTable1Val = new StringBuffer();
String templateMailBodyImageKey = "imageResourceName";
String templateMailBodyImageVal = "logo_cherryshoe.png";
// table 1 get data
for (int i = 0; i < 5; i++) {
templateMailBodyTable1Val.append("<tr><td class=\"twenty-five\">Column 1 Data - ");
templateMailBodyTable1Val.append(i);
templateMailBodyTable1Val.append("</td>");
templateMailBodyTable1Val.append("<td class=\"ten\">Column 2 Data - ");
templateMailBodyTable1Val.append(i);
templateMailBodyTable1Val.append("</td>");
templateMailBodyTable1Val.append("<td class=\"twenty-five\">Column 3 Data - ");
templateMailBodyTable1Val.append(i);
templateMailBodyTable1Val.append("</td>");
templateMailBodyTable1Val.append("<td class=\"forty\">Column 4 Data - ");
templateMailBodyTable1Val.append(i);
templateMailBodyTable1Val.append("</td></tr>");
}
Map<String, Object> variables = new HashMap<String, Object>();
variables.put(templateMailBodyNameKey, templateMailBodyNameVal);
variables.put(templateMailBodyTable1Key, templateMailBodyTable1Val.toString());
variables.put(templateMailBodyImageKey, templateMailBodyImageVal);
Context context = new Context();
variables.forEach((name, value) -> context.setVariable(name, value));
String content = templateEngine.process(defaultMailTemplate, context);
MimeMessageHelper helper = new MimeMessageHelper(mail, true);
helper.setTo(defaultFrom);
helper.setReplyTo(defaultFrom);
helper.setFrom(defaultFrom);
helper.setSubject("CherryShoe Example Html Email - " + threadLocalDf.get().format(new Date()));
helper.setText(content, true); // make html email
// Add the inline image, must go after setText. Referenced from the
// mail template as "cid:${imageResourceName}"
imageIs = this.getClass().getClassLoader().getResourceAsStream("templates/" + templateMailBodyImageVal);
byte[] imageByteArray = IOUtils.toByteArray(imageIs);
final InputStreamSource imageSource = new ByteArrayResource(imageByteArray);
helper.addInline(templateMailBodyImageVal, imageSource, "image/png");
mailSender.send(mail);
} catch (MessagingException e) {
log.warn(e.getMessage());
} finally {
if (imageIs != null)
imageIs.close();
}
}
}
src/main/test/com/cherryshoe/email/service.SendEmailServiceITTestpackage com.cherryshoe.service.email;
import javax.inject.Named;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;
import com.cherryshoe.CherryShoeAppApplication;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CherryShoeAppApplication.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class })
@Transactional
@WebAppConfiguration
public class SendEmailServiceITTest {
@Autowired
@Named("sendEmailServiceSpringBootImpl")
private SendEmailService emailServiceSpringBoot;
@Test
public void sendHtmlEmailTest() throws Exception {
try {
emailServiceSpringBoot.sendHtmlEmail();
} catch (Exception e) {
e.printStackTrace();
}
}
}
No comments:
Post a Comment
I appreciate your time in leaving a comment!