dependencies {
implementation 'com.mandrillapp.wrapper.lutung:lutung:0.0.8'
implementation group: 'org.springframework.retry', name: 'spring-retry', version: '1.2.4.RELEASE'
}
Mandrill emails in Spring Boot Java
Upasana | December 26, 2019 | 4 min read | 634 views
In this article we will learn how to send Mandrill emails (plain text, rich text, with attachments) from Java and Spring Boot applications with error handling and retry logic.
-
Gradle setup
-
Send plain text email
-
Send multimedia rich-text emails
-
Send emails with attachment
-
Error handling and retry attempts
Configuration
Lutung is a Mandrill API client for Java.
We will include lutung
dependency in our build.gradle
file.
for maven users, please use the following:
<dependency>
<groupId>com.mandrillapp.wrapper.lutung</groupId>
<artifactId>lutung</artifactId>
<version>0.0.8</version>
</dependency>
We need to create a bean for MandrillAPI that will be used later on for sending emails.
@Configuration
public class MandrillConfig {
@Value("${mandrill.api.key}") (1)
String mandrillApiKey;
@Bean
public MandrillApi createMandrillApi() {
return new MandrillApi(mandrillApiKey);
}
}
1 | You need to obtain Mandrill API key from your mandrill account. |
Also, we will create a POJO for handling email attributes.
public class EmailMessage {
private String fromEmail;
private String fromName;
private List<String> to = new ArrayList<>();
private List<String> cc = new ArrayList<>();
private String subject;
private String body;
private String attachmentName;
private String attachmentContent;
//Getters and Setters omitted for brevity
}
Send plain text emails
Plain text emails can be easily sent using MandrillAPI
. We will create a service that will accept the payload and send email using MandrillAPI under the hood.
@Service
public class EmailService {
private static final Logger logger = LoggerFactory.getLogger(EmailService.class);
@Autowired
private MandrillApi mandrillApi;
public void sendEmail(EmailMessage emailMessage) {
MandrillMessage message = new MandrillMessage();
message.setSubject(emailMessage.getSubject());
message.setText(emailMessage.getBody());
message.setAutoText(true);
message.setFromEmail(emailMessage.getFromEmail());
message.setFromName(emailMessage.getFromName());
ArrayList<MandrillMessage.Recipient> recipients = new ArrayList<>();
for (String email : emailMessage.getTo()) {
MandrillMessage.Recipient recipient = new MandrillMessage.Recipient();
recipient.setEmail(email);
//recipient.setName("optional name");
recipient.setType(MandrillMessage.Recipient.Type.TO);
recipients.add(recipient);
}
for (String email : emailMessage.getCc()) {
MandrillMessage.Recipient recipient = new MandrillMessage.Recipient();
recipient.setEmail(email);
recipient.setType(MandrillMessage.Recipient.Type.CC);
recipients.add(recipient);
}
message.setTo(recipients);
message.setPreserveRecipients(true);
try {
logger.info("Sending email to - {} with subject {}", emailMessage.getTo(), emailMessage.getSubject());
MandrillMessageStatus[] messageStatusReports = mandrillApi.messages().send(message, false);
for (MandrillMessageStatus messageStatusReport : messageStatusReports) {
final String status = messageStatusReport.getStatus();
logger.info("MessageStatusReports = " + status);
if (status.equalsIgnoreCase("rejected") || status.equalsIgnoreCase("invalid")) {
logger.error("Could not send email to {} status {}", emailMessage.getTo(), status);
}
}
} catch (MandrillApiError mandrillApiError) {
logger.error("MandrillApiError: " + mandrillApiError.getMandrillErrorAsJson());
logger.error("MandrillApiError sending email - " + emailMessage.getTo(), mandrillApiError);
throw new EmailException("MandrillApiError sending email - " + emailMessage.getTo(), mandrillApiError);
} catch (IOException e) {
logger.error("IOException sending email - " + emailMessage.getTo(), e);
throw new EmailException("IOException sending email - " + emailMessage.getTo(), e);
}
}
}
Now we are ready with our service that can send email to the customers.
Client code that will invoke this service will look like below:
@Component
public class EmailClient {
private final Logger logger = LoggerFactory.getLogger(WbcEmailService.class);
@Autowired
private EmailService emailService;
public void sendEmail() {
EmailMessage message = EmailMessageBuilder.anEmailMessage()
.withSubject("subject line")
.withBody("this is email text body")
.withTo("foo.bar@mail.com")
.withFrom("mandrill@foo.bar")
.withFromName("Foo Bar")
.build();
emailService.sendEmail(message);
}
}
Send multimedia richtext emails
Code for sending multi-media richtext email is quite similar to what it was for plain text email, except the few changes that we need to make.
We need to replace the following code in previously declared EmailService
:
message.setText(emailMessage.getBody());
message.setAutoText(true);
with this one:
message.setHtml(emailMessage.getBody());
message.setAutoHtml(true);
That will send richtext emails.
Send emails with attachment
Lets make few additions to the email service to make it support attachments.
Add the below code to sendEmail(…)
method of EmailService
class.
if (emailMessage.getAttachmentContent() != null) {
MandrillMessage.MessageContent messageContent = new MandrillMessage.MessageContent();
messageContent.setContent(emailMessage.getAttachmentContent());
messageContent.setBinary(true);
messageContent.setName(emailMessage.getAttachmentName());
message.setAttachments(Collections.singletonList(messageContent));
}
Including attachments in email is bit tricky when it comes to MandrillAPI, as we have to convert the attachment content to base64 format first.
To convert file content to Base64 format, we can leverage the following Java 8 based code:
private String convertFileToBase64(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
Now we can make changes in the client code to include attachment and trigger the email.
public void sendEmail() throws IOException {
final EmailMessage message = EmailMessageBuilder.anEmailMessage()
.withSubject("test")
.withBody("this is a test body")
.withTo("foo@email.com")
.withFrom("mandrill@foo.bar")
.withAttachmentName("foo-bar.pdf")
.withAttachmentContent(convertFileToBase64(Files.readAllBytes(Paths.get("foo-bar.pdf"))))
.build();
emailService.sendEmail(message);
}
Error handling and retry attempts
When it comes to HTTP communication, error can be obvious due to many reasons beyond our control. For example, network connection may become flaky or the remote service may become unavailable momentarily.
To know the exact reason for failure, MandrillAPI provides proper failure reason, which can be obtained using the below exception.
catch (MandrillApiError mandrillApiError) {
logger.error("MandrillApiError: " + mandrillApiError.getMandrillErrorAsJson(), mandrillApiError);
}
getMandrillErrorAsJson()
method returns the exact failure reason from Mandrill server.
if the failure is transient (which can be recovered by a retry, and not related to account) and is worth retrying, we can utilize Spring retry module to retry the attempt automatically with exponential backoff strategy.
@Configuration
@EnableRetry (1)
public class MandrillConfig {
}
1 | this will enable retry logic using spring-retry module. |
Finally we need to modify our EmailSerive.sendEmail(…)
method a bit to include the retry annotation.
@Retryable(maxAttempts = 3, value = {Exception.class}, backoff = @Backoff(value = 60000, multiplier = 2))
public void sendEmail(EmailMessage emailMessage) {
}
that’s all for this article.
Top articles in this category:
- Spring Boot with GMAIL SMTP
- Top 50 Spring Interview Questions
- Multi-threading Java Interview Questions for Investment Bank
- Sapient Global Market Java Interview Questions and Coding Exercise
- Hibernate & Spring Data JPA interview questions
- Goldman Sachs Java Interview Questions
- Cracking core java interviews - question bank