I have so far worked with two ways of sending emails from Java applications. The first way directly uses the JavaMail API, while the second one utilizes the Spring framework, which makes it a bit easier to work with the JavaMail API. The JavaMail Wikipedia page has sufficient examples for the direct use of the JavaMail API. This post shows code examples of the Spring one. We are implementing a Spring-based REST service for sending emails.

Maven dependencies

The Spring support for emails is provided in the spring-context-support artifact, and we also need the JavaMail artifact. Therefore, the following dependencies are needed in the pom.xml file:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>

The dependency dumbster provides a fake SMTP server, to be used in the unit tests:

<dependency>
    <groupId>com.github.kirviq</groupId>
    <artifactId>dumbster</artifactId>
    <version>1.7.1</version>
    <scope>test</scope>
</dependency>

The mail sending service implementation

We implement the following MailService interface, which can be used by, e.g., a Spring RestController to expose an email-sending REST service.

package io.github.ouyi.mail.service;

import javax.mail.MessagingException;

public interface MailService {
    void sendMail(String to, String subject, String bodyText, String attachmentPath) throws MessagingException;
}

The implementation looks like this:

package io.github.ouyi.mail.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Optional;

public class MailServiceImpl implements MailService {

    private final JavaMailSender mailSender;

    private final String from;

    @Autowired
    public MailServiceImpl(JavaMailSender mailSender, String from) {
        this.mailSender = mailSender;
        this.from = from;
    }

    @Override
    public void sendMail(String to, String subject, String bodyText, String attachmentPath) throws MessagingException {
        send(from, to, subject, bodyText, attachmentPath, !Optional.ofNullable(attachmentPath).orElse("").isEmpty());
    }

    private void send(String from, String to, String subject, String bodyText, String attachmentPath, boolean multipart) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, multipart);

        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(bodyText);

        if (!Optional.ofNullable(attachmentPath).orElse("").isEmpty()) {
            FileSystemResource file = new FileSystemResource(attachmentPath);
            helper.addAttachment(file.getFilename(), file);
        }
        mailSender.send(message);
    }
}

Note a multipart message is required in order to send an email with attachment.

Unit tests

As mentioned earlier, we use dumbster for unit testing:

package io.github.ouyi.mail.service;

import com.dumbster.smtp.SimpleSmtpServer;
import com.dumbster.smtp.SmtpMessage;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

import static org.junit.Assert.assertEquals;

public class MailServiceImplTest {

    private static final Logger logger = LoggerFactory.getLogger(MailServiceImplTest.class);
    private static final String HEADER_KEY_TO = "To";
    private static final String HEADER_KEY_SUBJECT = "Subject";

    @Test
    public void testSendMail() throws Exception {
        String smtpHost = "localhost";
        int smtpPort = 12345;

        String subject = "MY_SUBJECT";
        String messageBody = "MY_MESSAGE_BODY";
        String fromAddr = "sender@localhost";
        String toAddr = "test@test.com";

        logger.info("Start dumbster fake SMTP server...");
        SimpleSmtpServer server = SimpleSmtpServer.start(12345);

        logger.info("Send mail message");

        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        Properties properties = new Properties();
        properties.setProperty("mail.smtp.host", smtpHost);
        properties.setProperty("mail.smtp.port", String.valueOf(smtpPort));
        mailSender.setJavaMailProperties(properties);
        MailServiceImpl mailService = new MailServiceImpl(mailSender, fromAddr);

        mailService.sendMail(toAddr, subject, messageBody, null);
        server.stop();

        assertEquals(1, server.getReceivedEmails().size());

        SmtpMessage email = server.getReceivedEmails().get(0);

        assertEquals(toAddr, email.getHeaderValue(HEADER_KEY_TO));
        assertEquals(subject, email.getHeaderValue(HEADER_KEY_SUBJECT));
        assertEquals(messageBody, email.getBody());
    }

}

Dependency injection

Since we use Spring, we can use dependency injection to create and configure a JavaMailSenderImpl instance, which can be injected into the MailServiceImpl instance:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="my.smtp.host"/>
        <property name="port" value="587"/>
        <property name="username" value="user"/>
        <property name="password" value="pass"/>

        <property name="javaMailProperties">
            <props>
                <prop key="mail.smtp.auth">true</prop>
                <prop key="mail.smtp.starttls.enable">true</prop>
            </props>
        </property>
    </bean>
    <bean id="mailService" class="com.hellokoding.account.service.MailServiceImpl">
        <constructor-arg ref="mailSender"></constructor-arg>
        <constructor-arg type="java.lang.String" value="sender@mydomain.com"></constructor-arg>
    </bean>
</beans>

The REST controller

package io.github.ouyi.web;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.ws.rs.core.Response;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MailController {

    private final Logger logger = LoggerFactory.getLogger(MailController.class);

    @Autowired
    private MailService mailService;

    @Autowired
    private String mailTo;

    private static final String MAIL_SUBJECT = "medshop order";

    private static final String MAIL_CONTENT = "Please find the order in the attachment.";

    @RequestMapping(value = {"/sendmail"}, method = RequestMethod.POST, produces = "application/json")
    public @ResponseBody Response sendmail(HttpServletRequest request, HttpServletResponse response) throws Exception {

        File tempFile = null;
        OutputStream outputStream = null;
        try {
            tempFile = File.createTempFile("mail-attachment-", ".xlsx");
            outputStream = new BufferedOutputStream(new FileOutputStream(tempFile));
            XSSFWorkbook document = fileService.createSpreadsheet();
            document.write(outputStream);
            outputStream.flush();
            mailService.sendMail(mailTo, MAIL_SUBJECT, MAIL_CONTENT, tempFile.getAbsolutePath());
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
            if (tempFile != null) {
                tempFile.delete();
            }
        }
        return Response.ok().build();
    }
}

The sendmail method of the MailController has to return a Response object. When I changed it to void, I got the following error response from my GlassFish server:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>GlassFish Server Open Source Edition  5.0  - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 405 - Request method &amp;#39&#59;GET&amp;#39&#59; not supported</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Request method &amp;#39&#59;GET&amp;#39&#59; not supported</p><p><b>description</b>The specified HTTP method is not allowed for the requested resource.</p><hr/><h3>GlassFish Server Open Source Edition  5.0 </h3></body></html>

When it returns a Response object but without the annotation @ResponseBody, I got another error response:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>GlassFish Server Open Source Edition  5.0  - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 404 - Not Found</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Not Found</p><p><b>description</b>The requested resource is not available.</p><hr/><h3>GlassFish Server Open Source Edition  5.0 </h3></body></html>

Calling the REST service using JQuery and Ajax

Our mail-sending REST service can be consumed by the frontend JavaScript code as follows:

$(document).ready(function(event) {
    $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
      jqXHR.setRequestHeader("${_csrf.headerName}", "${_csrf.token}");
    });

    $('#sendmail-button').click(function() {
        $.ajax({
            type: 'post',
            url: "${contextPath}/sendmail",
            dataType : 'json'
        }).done(function(response) {
            console.log(response);
            alert("Email sent successfully!");
        }).fail(function(response) {
            console.log(response);
            alert("Failed to send email!");
        });
    });
});

Note that ${_csrf.headerName}, ${_csrf.token}, and ${contextPath} are templated on the server-side using jsp. If server-side templating is not used, the CSRF header name and token can be passed to the client as meta tags in the HTML head section, which can then be retrieved by JavaScript code. Details can be found in the Spring security documentation. The context path, which is quite static, can be easily injected into the frontend code at build time.