Chapter 16

JavaMail

JavaMail is a Java technology for sending and receiving emails. It is included in the Java EE platform but not in the Java SE platform. JavaMail depends on JavaBeans Activation Framework (JAF), a Java technology for determining the type of arbitrary data and encapsulating access to the data. Fortunately, JAF is bundled with JDK 1.6. If you are using Java SE 6 or later, the only API you need to download to work with JavaMail is the JavaMail API itself.

This chapter explains JavaMail and shows how to send and receive emails, work with folders and send and retrieve attachments.

JavaMail Overview

Sending and receiving email over the Internet or intranet are governed by several protocols. The Simple Mail Transfer Protocol (SMTP) is the protocol for sending emails. An SMTP server is a service that implements the SMTP protocol. An SMTP server may or may not be able to receive and store emails. Most SMTP servers today require you to authenticate yourself before you can use them. In addition, communications between your computer and the server may be encrypted using SSL or TLS. You should use TLS and not SSL since SSL has a debilitating vulnerability. If you have a Gmail account, you can use one of Google’s SMTP servers to send emails. In fact, the examples in this chapter use Gmail. Google has disabled access through SSL and you can only connect to their SMTP servers through TLS.

Note

For all intents and purposes, SSL is dead after Google publicly announced they found a vulnerability on SSL 3.0 on October 14, 2014. The Google security engineers who discovered this vulnerability called their finding POODLE (Padding Oracle On Downgraded Legacy Encryption). It is a man-in-the-middle exploit which takes advantage of Internet and security software clients’ fallback to SSL 3.0.

To store and receive emails you use either POP3 or IMAP. POP3 stands for Post Office Protocol 3 and IMAP stands for Internet Message Access Protocol. IMAP is the newer and more efficient one of the two.

The latest version of JavaMail, version 1.5, is the product of JSR 919. It understands SMTP, POP3 and IMAP and its official reference implementation of JavaMail can be downloaded from this site:

https://java.net/projects/javamail/pages/Home

The software is packaged in a javax.mail.jar file and you have to download it before you can start sending and receiving emails from within a Java program.

The types in the javax.mail package ad its subpackages encapsulate the JavaMail API. Here are some of the more important types:

  • javax.mail.Session. Represents a mail session.
  • javax.mail.Message. An abstract class that models an email message.
  • javax.mail.Transport. A template for creating objects responsible for transporting emails.
  • javax.mail.Store. An abstract class representing a message store and its access protocol. Subclasses include POP3Store and IMAPStore. A Store contains folders.
  • javax.mail.Folder. Represents a folder in the email system.
  • javax.mail.internet.InternetAddress. Represents an email address.
  • javax.mail.internet.MimeMessage. A subclass of Message that represents a MIME message.

The complete Java Doc for the API can be found here:

https://javamail.java.net/nonav/docs/api/

To send an email, you compose a Message and attach an InternetAddress to it. You then open a Session to an SMTP server. You then use a Transport to send the message.

To check or receive emails, you determine the folder in the email system from which you want to retrieve the emails. Next, you open a Session to the target POP3 or IMAP server, obtain the Store of the email system and call the getFolder method on the Store to get a Folder. Calling getMessages on the Folder gives you an array of email messages in the folder. Alternatively, you can call the getMessage method on the Folder to retrieve an single message.

The following sections provide examples. All examples can be found in the javamailweb application accompanying this book.

Sending Emails with JavaMail

To send an email using JavaMail, you need access to an SMTP server. You normally connect to an SMTP server through port 587 or 25. However, many SMTP servers do use different ports and you have to get these details before you can start sending emails through them.

The JavaMailSender class in Listing 16.1 shows you how to send an email. The code is not complete, you have to type in the details for the SMTP server as well as the sender’s and recipient’s email addresses. Note that some SMTP servers insist that the sender uses the same email as the SMTP username used to access the SMTP server. For example, if you are using the username [email protected] to connect to an SMTP server, the server may only transport your email if the sender’s email is also [email protected].

You cannot test this example if you do not have access to an SMTP server. Don’t worry, a later section of this chapter talks about using Google’s SMTP server to send emails.

Listing 16.1: Sending email with JavaMail

package util;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class JavaMailSender {

    public void sendEmail() {
        String to = "...";//to email
        String from = "...";//from email
        final String username = "username";
        final String password = "password";

        String host = "smtp.gmail.com";
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.port", "..."); // smtp port

        // Get the Session object.
        Session session = Session.getInstance(props,
            new javax.mail.Authenticator() {
                protected PasswordAuthentication 
                        getPasswordAuthentication() {
                    return new PasswordAuthentication(
                            username, password);
                }
            });

        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.setRecipients(Message.RecipientType.TO,
                    InternetAddress.parse(to));
            message.setSubject("You got mail");
            message.setText("This email was sent with JavaMail.");
            Transport.send(message);
            System.out.println("Email sent successfully...");
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        JavaMailSender sender = new JavaMailSender();
        sender.sendEmail();
    }
}

To send an email, you first put the details of the SMTP server in a Properties and then create a Session instance by passing the Properties. Most of the time, you also need to pass a username and a password. The boilerplate code for creating a Session is as follows.

        Session session = Session.getInstance(props,
            new javax.mail.Authenticator() {
                protected PasswordAuthentication 
                        getPasswordAuthentication() {
                    return new PasswordAuthentication(
                            username, password);
                }
            });

Next, create a MimeMessage and set its from, subject, text and recipients properties.

            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.setRecipients(Message.RecipientType.TO,
                    InternetAddress.parse(to));
            message.setSubject("You got mail");
            message.setText("This email was sent with JavaMail.");

Once the message is ready, send it by calling the Transport class’s send method.

            Transport.send(message);

Sending Emails with Apache Commons Email

Sending email directly using JavaMail is not very straightforward. For example, you cannot simply pass an email address as a string to the Message. Instead, you have to construct an InternetAddress. On top of that, authentication costs you six lines of code. The Apache Commons Mail library provides an easier way to send email. In many cases, it is the preferred method too over using JavaMail directly. This library uses JavaMail internally and does not support email checking or retrieval.

You can download the library from here:

http://commons.apache.org/proper/commons-email/

Listing 16.2 shows a class that uses Apache Commons Email to send an email. Notice how simpler it is compared to using JavaMail directly? You need to enter the SMTP details and the sender’s and recipient’s emails to test it. If you do not have access to an SMTP server, hang on tight. The next section explains how you can use Google’s SMTP server.

Listing 16.2: Using Commons Email to send an email

package util;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;

public class CommonsEmailSender {

    public void sendEmail() {
        Email email = new SimpleEmail();
        email.setHostName("...");
        email.setSmtpPort(...);
        email.setAuthenticator(
                new DefaultAuthenticator("username", "password"));
        email.setStartTLSEnabled(true);
        try {
            email.setFrom("[sender email]");
            email.setSubject("You got commons mail");
            email.setMsg("Sent using Commons Email...");
            email.addTo("[recipient email]");
            email.send();
            System.out.println("Message sent.");
        } catch (EmailException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        CommonsEmailSender sender = new CommonsEmailSender();
        sender.sendEmail();
    }
}

Basically, Apache Commons Email hides the complexity of JavaMail when sending an email. Instead of creating a MimeMessage, you create a SimpleEmail instance, passing the SMTP details directly to it instead of to a Session object. Also, you can pass email addresses as strings and there is no need for them to be encapsulated in InternetAddress objects. Transport has been hidden to. You can send the email by calling its send method.

Using Gmail's SMTP Server

Okay, here is an important part for those without a personal SMTP server. If you have a Gmail account, you can use Google’s public SMTP server. You can find the details here:

https://support.google.com/a/answer/176600?hl=en

At the time of writing, the SMTP host is smtp.gmail.com and the port is 587. You should check the web site to get the latest SMTP host and port. In addition, there is more to do before you can start using it.

By default, Gmail does not allow you to sign in to your email account using an insecure channel. As such, you will not be able to programmatically use the Gmail SMTP server to send emails. To loosen this restriction, visit this web page while logged in to your Gmail account:

https://www.google.com/settings/security/lesssecureapps

Then, click Turn On to allow less secure apps.

The Gmail SMTP server does not support SSL anymore after the POODLE announcement.

WebMail

The javamailweb application provides a web interface that allows you to check your email using a web browser. You can use this application with any POP3 or IMAP server, even though this example uses Gmail through IMAP. If you want to use this example with a Gmail account, first make sure that IMAP access is enabled. To do this, log in to your Gmail account, open Settings, click the Forwarding and POP/IMAP tab and make sure IMAP in enabled.

Following good design practice, the application adopts the Model 2 architecture with a servlet as its controller. It uses several action classes that encapsulate the functions offered by the application. In addition, it employs a filter to check if the user is logged in and forwards unauthorized users to the Login page.

As usual, I start by presenting some screenshots of the application. I believe that you can get a good grasp of what functionality the application offers by looking at the images in Figure 16.1 to Figure 16.5.

Figure 16.1 shows the Login form for the application. You use your (Gmail) email address and password to login. Upon a successful login, your email and password will be stored in your HttpSession.

Figure 16.1: The Login form

Figure 16.2 shows the main page. It shows twenty messages per page and you can navigate to other pages by clicking the green arrow buttons at the top-right corner of the page. By default, the content of the INBOX folder will be displayed, however you can check the emails in other folders too by clicking the links to those folders in the left hand side menu. Note that it also shows the number of messages in the active folder.

Figure 16.2: The main page

Figures 16.3 shows the details of a message that has no attachments. Figure 16.4 shows a message with attachments. You can click on the attachment links to download the attachments.

Figure 16.3.: The message details

Figure 16.4: A message with attachments

Finally, Figure 16.5 shows the page for composing an email. It does not support uploading attachments, unfortunately, but feel free to extend it yourself.

Figure 16.5: The compose page

The next subsections discuss the components in the application.

The Deployment Descriptor

The deployment descriptor is given in Listing 16.3 and it does not have much. In fact, it only specifies a welcome-file-list element to make the path /main, which is mapped to the servlet controller, the default resource.

Listing 16.3: The deployment descriptor

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
        version="3.1">
    <welcome-file-list>
        <welcome-file>main</welcome-file>
    </welcome-file-list>
</web-app>

The Filter

The CheckSessionFilter class in Listing 16.4 is a servlet filter that is applied to all paths and checks if the current user is logged in. The HttpSession of an authorized user has a non-null username attribute. The login page and the CSS file are not filtered.

Listing 16.4: The CheckSessionFilter class

package filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@WebFilter(urlPatterns = {"/*"})
public class CheckSessionFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws
            ServletException {
    }

    @Override
    public void doFilter(ServletRequest request,
            ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpServletRequest
                = (HttpServletRequest) request;
        String uri = httpServletRequest.getRequestURI();
        int lastIndex = uri.lastIndexOf("/");
        String action = uri.substring(lastIndex + 1);
        if (!action.equals("login") && !action.isEmpty()
                && !action.endsWith(".css")) {
            // must have HttpSession
            HttpSession session = httpServletRequest.getSession();
            if (session.getAttribute("userName") == null) {
                RequestDispatcher rd = request.
                        getRequestDispatcher("/login");
                rd.forward(request, response);
                return;
            }
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

The Model

JavaMail objects are limited to the action classes and this is done on purpose. Objects such as JavaMail Message and Folder objects may no longer be accessible once their Store and Session are closed. Instead, the classes in Listings 16.5 to 16.8 are provides.

The MessageBean class in Listing 16.5 is a POJO that models an email message. It contains id, from, subject, text, receivedDate and attachments properties. The attachments property contains an array of paths to the message attachments rather than the attachments themselves.

Listing 16.5: The MessageBean class

package model;
import java.io.Serializable;
import java.time.LocalDateTime;

public class MessageBean implements Serializable {
    private long id;
    private String from;
    private String subject;
    private String text;
    private LocalDateTime receivedDate;
    private String[] attachments;

    public MessageBean() {
    }
    public MessageBean(String from, String subject, String text) {
        this.from = from;
        this.subject = subject;
        this.text = text;
    }

    // get and set methods not shown to save space
}

The FolderBean class in Listing 16.6 represents an email folder. It has only two properties, name and fullName. The folder name is normally a label or display name. The folder full name contains the full path to the folder and is used to access the folder.

Listing 16.6: The FolderBean class

package model;
public class FolderBean {
    private String name;
    private String fullName;
    
    public FolderBean() {
        
    }
    public FolderBean(String name, String fullName) {
        this.name = name;
        this.fullName = fullName;
    }

    // get and set methods not shown to save space
}

The FetchResponse class in Listing 16.7 encapsulates several other objects and is used to return all objects necessary to display the main page. For example, the page requires the list of folders as well as messages in the current folder. The FetchResponse class can fulfill this need.

Listing 16.7: The FetchResponse class

package model;
import java.util.List;

public class FetchResponse {
    private long messageCount;
    private String folder = "INBOX";
    private MessageBean messageBean;
    private List<MessageBean> messageBeans;
    private List<FolderBean> folderBeans;
    
    // get and set methods not shown to save space
}

The SmtpServer class in Listing 16.8 contains the details of an SMTP server. An instance of this class is used to transfer the SMTP server details from the controller servlet to an action class used for sending emails.

Listing 16.8: The SmtpServer class

package model;
public class SmtpServer {
    private String host;
    private int port;
    private String userName;
    private String password;

    public SmtpServer() {
        
    }
    public SmtpServer(String host, int port, 
            String userName, String password) {
        this.host = host;
        this.port = port;
        this.userName = userName;
        this.password = password;
    }

    // get and set methods not shown to save space    
}

The Controller Servlet

The ControllerServlet class in Listing 16.9 controls the flow of the application. Its init method opens a config.properties file in WEB-INF and reads its content to the smtpHost, smtpPort, mailStoreProtocol and mailStoreHost class variables. The smtpHost and smtpPort variables contain the SMTP host and port, respectively. The value of mailStoreProtocol is either POP3 or IMAP and mailStoreHost contains the host of the POP3/IMAP server.

The default config.properties file is given in Listing 16.10. You can change its values with a text editor and restart the servlet container for the new values to take effect.

Listing 16.9: The ControllerServlet class

package controller;
import action.FetchEmailAction;
import action.GetAttachmentAction;
import action.GetMessageAction;
import action.SendEmailAction;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import model.FetchResponse;
import util.Constants;

@WebServlet(name = "ControllerServlet",
        urlPatterns = {"/login", "/main", "/message",
            "/compose", "/send", "/attachment"})
public class ControllerServlet extends HttpServlet {

    private String smtpHost;
    private int smtpPort;
    private String mailStoreProtocol;
    private String mailStoreHost;

    @Override
    public void init(ServletConfig servletConfig)
            throws ServletException {
        ServletContext servletContext
                = servletConfig.getServletContext();
        String configFilePath = servletContext
                .getRealPath("/WEB-INF/config.properties");
        Properties props = new Properties();
        try (FileInputStream in = new FileInputStream(configFilePath)) {
            props.load(in);
        } catch (IOException e) {

        }
        smtpHost = props.getProperty("smtp.host");
        try {
            smtpPort = Integer.parseInt(
                    props.getProperty("smtp.port"));
        } catch (NumberFormatException e) {
        }
        mailStoreHost = props.getProperty(
                "mail.store.host");
        mailStoreProtocol = props.getProperty(
                "mail.store.protocol");
    }

    protected void processRequest(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        String uri = request.getRequestURI();
        if (uri.contains("/css/")) {
            return;
        }
        int lastIndex = uri.lastIndexOf("/");
        String action = uri.substring(lastIndex + 1);
        String dispatchUrl = null;
        boolean redirect = false;
        String folder = request.getParameter("folder");
        if (folder == null || folder.isEmpty()) {
            folder = "INBOX";
        }
        HttpSession session = request.getSession();
        switch (action) {
            case "":
                dispatchUrl = "/WEB-INF/jsp/Login.jsp";
                break;
            case "login":
                String userName = request.getParameter("userName");
                String password = request.getParameter("password");
                session.setAttribute("userName", userName);
                session.setAttribute("password", password);
            // no break here so "main" will be called
            case "main":
                request.setAttribute("folder", folder);
                userName = (String) session.getAttribute("userName");
                password = (String) session.getAttribute("password");
                FetchEmailAction fetchMailAction = new
                        FetchEmailAction();
                String pageString = request.getParameter("page");
                int page = 1;
                if (pageString != null && !pageString.isEmpty()) {
                    try {
                        page = Integer.parseInt(pageString);
                    } catch (Exception e) {
                    }
                }
                try {
                    FetchResponse fetchResponse = fetchMailAction
                            .fetchEmails(mailStoreProtocol,
                                    mailStoreHost, userName,
                                    password, folder, page);
                    request.setAttribute("page", page);
                    long messageCount = fetchResponse.getMessageCount();
                    int maxPage = (int) Math.ceil(0.5 + messageCount
                            / Constants.ROWS_PER_PAGE);
                    request.setAttribute("rowsPerPage",
                            Constants.ROWS_PER_PAGE);
                    request.setAttribute("maxPage", maxPage);

                    request.setAttribute("messageCount", messageCount);
                    request.setAttribute("messages", fetchResponse.
                            getMessageBeans());
                    request.setAttribute("folders", fetchResponse.
                            getFolders());
                    dispatchUrl = "/WEB-INF/jsp/Main.jsp";
                } catch (Exception e) {
                    System.out.println("error:" + e.getMessage());
                    dispatchUrl = "/WEB-INF/jsp/Login.jsp";
                }
                break;
            case "message":
                userName = (String) session.getAttribute("userName");
                password = (String) session.getAttribute("password");
                request.setAttribute("folder", folder);
                String idString = request.getParameter("id");
                int id = 0;
                try {
                    id = Integer.parseInt(idString);
                } catch (NumberFormatException e) {

                }
                GetMessageAction getMessageAction = new
                        GetMessageAction();
                try {
                    FetchResponse fetchResponse = getMessageAction.
                            getMessage(
                                    mailStoreProtocol, mailStoreHost,
                                    userName, password, folder, id);
                    request.setAttribute("messageCount", fetchResponse.
                            getMessageCount());
                    request.setAttribute("message", fetchResponse.
                            getMessageBean());
                    request.setAttribute("folders", fetchResponse.
                            getFolders());
                    dispatchUrl = "/WEB-INF/jsp/Message.jsp";
                } catch (Exception e) {
                    dispatchUrl = "/WEB-INF/jsp/Login.jsp";
                }
                break;
            case "compose":
                dispatchUrl = "/WEB-INF/jsp/Compose.jsp";
                break;
            case "send":
                SendEmailAction sendEmailAction = new SendEmailAction();
                String toEmail = request.getParameter("to");
                String subject = request.getParameter("subject");
                String text = request.getParameter("text");
                userName = (String) session.getAttribute("userName");
                password = (String) session.getAttribute("password");
                try {
                    sendEmailAction.sendEmail(smtpHost, smtpPort,
                            userName, password, toEmail, subject, text);
                    redirect = true;
                } catch (Exception e) {
                    request.setAttribute("error", e.getMessage());
                    dispatchUrl = "/WEB-INF/jsp/Error.jsp";
                }
                break;
            case "attachment":
                userName = (String) session.getAttribute("userName");
                password = (String) session.getAttribute("password");
                String messageIdString = request.getParameter("mid");
                int messageId = -1;
                try {
                    messageId = Integer.parseInt(messageIdString);
                } catch (Exception e) {
                }
                String attachmentFileName = request.getParameter("file");
                GetAttachmentAction getAttachmentAction = new
                        GetAttachmentAction();
                try (InputStream inputStream = getAttachmentAction
                        .getAttachment(mailStoreProtocol,
                                mailStoreHost, userName,
                                password, folder, messageId,
                                attachmentFileName);
                        BufferedInputStream bis = new 
                                BufferedInputStream(inputStream);
                        OutputStream os = response.getOutputStream()) {
                    response.setContentType("APPLICATION/OCTET-STREAM");
                    response.addHeader("Content-Disposition",
                            "attachment; filename=" + 
                            attachmentFileName);
                    byte[] buffer = new byte[1024];
                    int i = bis.read(buffer);
                    while (i != -1) {
                        os.write(buffer, 0, i);
                        i = bis.read(buffer);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return;
            default:
                dispatchUrl = "/WEB-INF/jsp/Error.jsp";
                break;
        }
        if (redirect) {
            response.sendRedirect("main");
        } else {
            RequestDispatcher rd
                    = request.getRequestDispatcher(dispatchUrl);
            rd.forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
}

Both the servlet’s doGet and doPost method simply call the private processRequest method. The latter in turn uses a switch statement to direct the HTTP call to the appropriate view or action object.

Listing 16.10: The config.properties file

smtp.host=smtp.gmail.com
smtp.port=587
mail.store.protocol=imaps
mail.store.host=imap.gmail.com

The Action Classes

Listing 16.11 to Listing 16.14 shows the action classes for the application. The SendEmailAction class in Listing 16.11 is responsible for sending emails. It exposes a sendEmail method that uses a utility class (CommonsEmailSender) to do the work.

Listing 16.11: The SendEmailAction class

package action;
import util.CommonsEmailSender;
import model.SmtpServer;

public class SendEmailAction {
    public void sendEmail(String smtpHost, int smtpPort,
            String userName, String password, String to, 
            String subject, String message) throws Exception {
        CommonsEmailSender sender = new CommonsEmailSender();
        SmtpServer smtpServer = new SmtpServer(smtpHost,
                smtpPort, userName, password);
        String[] toEmails = {to};
        sender.sendEmail(smtpServer, userName, 
                toEmails, /*ccEmails=*/null, /*bccEmails=*/null,
                subject, message);
    }
}

The FetchEmailsAction class in Listing 16.12 opens an email store and folder and reads the email in the specified page. Each page contains twenty messages. Again, the bulk of the work is delegated to a utility class (JavaMailUtil).

Listing 16.12: The FetchEmailsAction class

package action;
import model.FetchResponse;
import util.JavaMailUtil;

public class FetchEmailAction {

    public FetchResponse fetchEmails(String mailStoreProtocol,
            String mailStoreHost, String userName,
            String password, String folderName, int page) 
            throws Exception {
        return JavaMailUtil.fetchEmails(mailStoreProtocol,
                mailStoreHost, userName, password, folderName, page);
    }
}

The GetMessageAction class in Listing 16.13 fetches the details of a message. Again, it uses the JavaMailUtil class to do its job.

Listing 16.13: The GetMessageAction class

package action;
import model.FetchResponse;
import util.JavaMailUtil;

public class GetMessageAction {
    public FetchResponse getMessage(String mailStoreProtocol,
            String mailStoreHost, String userName, 
            String password, String folderName, int id) throws Exception {
        return JavaMailUtil.getMessage(mailStoreProtocol, 
                mailStoreHost, 
                userName, password, folderName, id);
    }
}

The GetAttachmentAction class, as the name implies, returns an email attachment. It takes the folder name, message identifier and the name of the attachment file. Like the action classes discussed earlier, GetAttachmentAction also call the JavaMtilUtil class.

Listing 16.14: The GetAttachmentAction class

package action;
import java.io.InputStream;
import util.JavaMailUtil;

public class GetAttachmentAction {
    public InputStream getAttachment(String mailStoreProtocol,
            String mailStoreHost, String userName, 
            String password, String folderName, int messageId,
            String attachmentFileName) throws Exception {
        return JavaMailUtil.getAttachment(mailStoreProtocol,
                mailStoreHost, userName, password, folderName,
                messageId, attachmentFileName);
    }
}

The Utility Classes

The action classes use two utility classes. These utility classes are given in Listing 16.15 and Listing 16.16. The CommonsEmailSender class in Listing 16.15 uses Apache Commons Email to send emails. The JavaMailUtil class in Listing 16.16 uses JavaMail directly and provides methods for fetching emails in the specified folder, retrieving the details of a message and getting an attachment.

Listing 16.15: The CommonsEmailSender class

package util;
import model.SmtpServer;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;

public class CommonsEmailSender {

    public void sendEmail(SmtpServer smtpServer, 
            String fromEmail, String[] toEmails, 
            String[] ccEmails, String[] bccEmails,
            String subject, String text) 
            throws EmailException {
        Email email = new SimpleEmail();
        email.setHostName(smtpServer.getHost());
        email.setSmtpPort(587);
        email.setAuthenticator(
                new DefaultAuthenticator(
                        smtpServer.getUserName(), 
                        smtpServer.getPassword()));
        email.setStartTLSEnabled(true);
        email.setFrom(fromEmail);
        email.setSubject(subject);
        email.setMsg(text);
        email.addTo(toEmails);
        if (ccEmails != null && ccEmails.length > 0) {
            email.addCc(ccEmails);
        }
        if (bccEmails != null && bccEmails.length > 0) {
            email.addBcc(bccEmails);
        }
        email.send();
    }
}

Listing 16.16: The JavaMailUtil class

package util;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import model.FetchResponse;
import model.FolderBean;
import model.MessageBean;

public class JavaMailUtil {

    public void sendEmail() {
        String to = "...";//to email
        String from = "[...]@gmail.com";//from email
        final String username = "username";
        final String password = "password";

        String host = "smtp.gmail.com";
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.port", "587");

        // Get the Session object.
        Session session = Session.getInstance(props,
                new javax.mail.Authenticator() {
                    protected PasswordAuthentication
                    getPasswordAuthentication() {
                        return new PasswordAuthentication(
                                username, password);
                    }
                });

        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.setRecipients(Message.RecipientType.TO,
                    InternetAddress.parse(to));
            message.setSubject("You got mail");
            message.setText("This email was sent with JavaMail.");
            Transport.send(message);
            System.out.println("Email sent successfully...");
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    public static FetchResponse fetchEmails(String protocol,
            String host,
            String userName, String password,
            String currentFolder, int page) throws Exception {
        FetchResponse fetchResponse = new FetchResponse();
        List<MessageBean> messageBeans = new ArrayList<>();
        fetchResponse.setMessageBeans(messageBeans);
        List<FolderBean> folderBeans = new ArrayList<>();
        fetchResponse.setFolders(folderBeans);
        try {
            Properties properties = new Properties();
            properties.put("mail.store.protocol", protocol);
            Session session = Session.getDefaultInstance(
                    properties);
            Store store = session.getStore(protocol);
            store.connect(host, userName, password);

            // get list of folders
            Folder defaultFolder = store.getDefaultFolder();
            Folder[] folders = defaultFolder.list();
            for (Folder folder : folders) {
                Folder[] subFolders = folder.list();
                if (subFolders.length == 0) {
                    folderBeans.add(new FolderBean(
                            folder.getName(), folder.
                            getFullName()));
                } else {
                    for (Folder subFolder : subFolders) {
                        folderBeans.add(new FolderBean(
                                subFolder.getName(), subFolder.
                                getFullName()));
                    }
                }
            }
            Folder folder = store.getFolder(currentFolder);
            folder.open(Folder.READ_ONLY);

            Message[] messages = folder.getMessages();
            fetchResponse.setMessageCount(messages.length);

            int length = messages.length;
            int maxNumber = length - Constants.ROWS_PER_PAGE
                    * (page - 1);
            int minNumber = Math.max(0, length
                    - Constants.ROWS_PER_PAGE * page);
            for (int i = maxNumber - 1; i >= minNumber; i--) {
                MessageBean msgBean = new MessageBean();
                Message message = messages[i];
                msgBean.setId(i);
                msgBean.setSubject(message.getSubject());
                msgBean.setFrom(message.getFrom()[0].toString());
                LocalDateTime rd = LocalDateTime.ofInstant(
                        message.getReceivedDate().toInstant(),
                        ZoneId.systemDefault());
                msgBean.setReceivedDate(rd);
                messageBeans.add(msgBean);
            }

            //close the store and folder objects
            folder.close(false);
            store.close();
        } catch (Exception e) {
            throw new Exception("Error: " + e.getMessage());
        }
        return fetchResponse;
    }

    public static FetchResponse getMessage(String protocol,
            String host,
            String userName, String password,
            String currentFolder, int id) throws Exception {
        FetchResponse fetchResponse = new FetchResponse();
        List<MessageBean> messageBeans = new ArrayList<>();
        fetchResponse.setMessageBeans(messageBeans);
        List<FolderBean> folderBeans = new ArrayList<>();
        fetchResponse.setFolders(folderBeans);
        try {
            Properties properties = new Properties();
            properties.put("mail.store.protocol", protocol);
            Session session = Session.getDefaultInstance(
                    properties);
            Store store = session.getStore(protocol);
            store.connect(host, userName, password);

            // get list of folders
            Folder defaultFolder = store.getDefaultFolder();
            Folder[] folders = defaultFolder.list();
            for (Folder folder : folders) {
                Folder[] subFolders = folder.list();
                if (subFolders.length == 0) {
                    folderBeans.add(new FolderBean(
                            folder.getName(), folder.
                            getFullName()));
                } else {
                    for (Folder subFolder : subFolders) {
                        folderBeans.add(new FolderBean(
                                subFolder.getName(), subFolder.
                                getFullName()));
                    }
                }
            }
            Folder folder = store.getFolder(currentFolder);
            folder.open(Folder.READ_ONLY);

            Message[] messages = folder.getMessages();
            fetchResponse.setMessageCount(messages.length);

            MessageBean msgBean = new MessageBean();
            fetchResponse.setMessageBean(msgBean);
            Message message = messages[id];
            msgBean.setId(id);
            msgBean.setSubject(message.getSubject());
            msgBean.setFrom(message.getFrom()[0].toString());
            LocalDateTime rd = LocalDateTime.ofInstant(
                    message.getReceivedDate().toInstant(),
                    ZoneId.systemDefault());
            msgBean.setReceivedDate(rd);

            String contentType = message.getContentType().
                    toLowerCase();
            if (contentType.contains("multipart")) {
                // this message may contain attachment
                Multipart multiPart = (Multipart) message.
                        getContent();
                List<String> attachments = new ArrayList<>();
                for (int i = 0; i < multiPart.getCount(); i++) {
                    MimeBodyPart part = (MimeBodyPart) multiPart.
                            getBodyPart(i);
                    if (Part.ATTACHMENT.equalsIgnoreCase(part.
                            getDisposition())) {
                        attachments.add(part.getFileName());
                    } else {
                        // a part that is not an attachment may 
                        // contain text, HTML or 
                        // multipart/alternative
                        String partContentType = part.
                                getContentType().toLowerCase();
                        if (partContentType.
                                contains("text/plain")
                                || partContentType.contains(
                                        "text/html")) {
                            msgBean.setText(part.getContent().
                                    toString());
                        } else if (partContentType.toLowerCase()
                                .contains("multipart/alternative")) {
                            // if a part's content type is
                            // multipart/alternative, 
                            // the part may contain multiparts that
                            // contain text or HTML or both
                            if (part.getContent() instanceof 
                                    MimeMultipart) {
                                MimeMultipart multiPart2 = 
                                    (MimeMultipart) part.getContent();
                                for (int j = 0; j < multiPart2.
                                        getCount(); j++) {
                                    // may contain both plain text and                            
                                    // HTML
                                    MimeBodyPart part2 = (MimeBodyPart) 
                                            multiPart2.getBodyPart(j);
                                    String contentType2 = part2.
                                            getContentType().
                                            toLowerCase();
                                    if (contentType2.contains(
                                            "text/plain")
                                            || contentType2.
                                            contains("text/html")) {
                                        // we will get the latter of 
                                        // plain text or html
                                        msgBean.setText(part2.
                                                getContent().
                                                toString());
                                    }
                                }
                            }
                        }
                    }
                }
                if (attachments.size() > 0) {
                    msgBean.setAttachments(attachments.toArray(
                            new String[0]));
                }
            } else if (contentType.contains("text/plain")
                    || contentType.contains("text/html")) {
                Object content = message.getContent();
                if (content != null) {
                    msgBean.setText(content.toString());
                }
            }

            //close the folder and store
            folder.close(false);
            store.close();
        } catch (Exception e) {
            throw new Exception("Error: " + e.getMessage());
        }
        return fetchResponse;
    }

    public static InputStream getAttachment(String protocol,
            String host,
            String userName, String password, String folderName,
            int messageId,
            String attachmentFileName)
            throws Exception {
        try {
            Properties properties = new Properties();
            properties.put("mail.store.protocol", protocol);
            Session session = Session.getDefaultInstance(
                    properties);
            Store store = session.getStore(protocol);
            store.connect(host, userName, password);

            // get list of folders
            Folder folder = store.getFolder(folderName);
            folder.open(Folder.READ_ONLY);

            Message[] messages = folder.getMessages();
            Message message = messages[messageId];

            String contentType = message.getContentType().
                    toLowerCase();
            if (contentType.contains("multipart")) {
                // this message may contain attachment
                Multipart multiPart = (Multipart) message.
                        getContent();
                for (int i = 0; i < multiPart.getCount(); i++) {
                    MimeBodyPart part = (MimeBodyPart) multiPart.
                            getBodyPart(i);
                    if (Part.ATTACHMENT.equalsIgnoreCase(part.
                            getDisposition())) {
                        if (attachmentFileName.equals(part.
                                getFileName())) {
                            return part.getInputStream();
                        }
                    }
                }
            }
            //close the folder and store
            folder.close(false);
            store.close();
        } catch (Exception e) {
            throw new Exception("Error: " + e.getMessage());
        }
        return null;
    }
}

In addition to the utility classes, there is also the Constants class in Listing 16.17. This class stores values (currently there is only one value) that are used by other classes.

Listing 16.17: The Constants class

package util;
public class Constants {
    public static int ROWS_PER_PAGE = 20;
}

The Views

All JSP pages are stored in the WEB-INF/jsp folder to prevent direct access to them.

Listings 16.18 to 16.23 present the views for the application. Listing 16.18 shows the Login.jsp page. It contains an HTML form for the user to log in.

Listing 16.18: The Login.jsp page

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
    <head>
        <title>Login</title>
        <link rel="stylesheet" type="text/css" href="<c:url value="/css/style.css"/>">
    </head>
    <body>
        <form action="login" method="post">
            <h1>Login
                <span>Please enter your username and password</span>
            </h1>
            <label>
                <span>User Name :</span>
                <input type="text" name="userName" 
                       placeholder="UserName or email" required/>
            </label>
            <label>
                <span>Password :</span>
                <input id="email" type="password" name="password" 
                       placeholder="Your password" required />
            </label>
            <label>
                <span>&nbsp;</span> 
                <input type="submit" value="Login"/> 
            </label>    
        </form>
    </body>
</html>

The Main.jsp page in Listing 16.19 uses JSTL to display the messages in the current page. In addition, the page includes an include file to display the list of folders. The include file is shown in Listing 16.20.

Listing 16.19: The Main.jsp page

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!DOCTYPE html>
<html>
<head>
    <title>Mail Web UI</title>
    <link rel="stylesheet" type="text/css" href="<c:url value="/css/style.css"/>">
</head>
<body>
    <%@include file="Folders.jspf"%>
    <div style="float:none;padding:10px;width:auto;overflow:hidden">
        <div style="text-align:right;padding:5px;color:teal">
            Messages ${1 + (page-1) * rowsPerPage} - ${(page-1) * rowsPerPage + fn:length(messages)}
            <c:if test="${page > 1}">
                <a class="composeButton" href="main?folder=${folder}&page=${page-1}"><</a>
            </c:if>
            <c:if test="${page < maxPage}">
                <a class="composeButton" href="main?folder=${folder}&page=${page+1}">></a></span>
            </c:if>
        </div>
        <table>
            <tr>
                <th>From</th>
                <th>Subject</th>
                <th>Received</th>
            </tr>
            <c:forEach var="message" items="${messages}">
                <tr>
                    <td><a href="message?id=${message.id}&folder=${param.folder}">${message.from}</a></td>
                    <td><a href="message?id=${message.id}&folder=${param.folder}">${message.subject}</a></td>
                    <c:set value="${message.receivedDate}" var="rd"/>
                    <td>${rd.monthValue}/${rd.dayOfMonth}/${rd.year} 
                        <fmt:formatNumber value="${rd.hour}" type="number" 
                            minIntegerDigits="2"/>:<fmt:formatNumber 
                            value="${rd.minute}" type="number" minIntegerDigits="2"/>
                    </td>
                </tr>
            </c:forEach>
        </table>
    </div>
</body>
</html>

Listing 16.20: The Folders.jspf page

<div style="float:left;border:1px solid gray;padding:10px">
    <p><a class="composeButton" href="compose">Compose</a></p>
    <c:forEach var="f" items="${folders}">
        <p style="padding:0px;margin:0px">
            <c:if test="${folder==f.fullName}">
                <a href="main?folder=${f.fullName}"
                 style="color:red;text-decoration:none;font-weight:bold"    
                    >${f.name} (${messageCount})
                </a>
            </c:if>
            <c:if test="${folder!=f.fullName}">
                <a href="main?folder=${f.fullName}">${f.name}</a>
            </c:if>
        </p>
    </c:forEach>
</div>

The Message.jsp page in Listing 16.21 displays the details of a selected message. It also includes the Folders.jspf page fragment to display the folder list.

Listing 16.21: The Message.jsp page

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
    <title>Message Details</title>
    <link rel="stylesheet" type="text/css" href="<c:url value="/css/style.css"/>">
</head>
<body>
    <%@include file="Folders.jspf"%>
    <div style="float:none;padding:10px;width:auto;overflow:hidden">
        <p>From: ${message.from}</p>
        <p>Subject: ${message.subject}</p>
        <c:if test="${message.attachments != null}">
            Attachments:
            <c:forEach var="attachment" items="${message.attachments}" varStatus="status">
                <a href="attachment?folder=${folder}&mid=${message.id}&file=${attachment}">
                    ${attachment}</a><c:if test="${!status.last}">,</c:if>
            </c:forEach>
        </c:if>
        <p>${message.text}</p>
    </div>
</body>
</html>

The Compose.jsp page in Listing 16.22 contains a form for sending an email.

Listing 16.22: The Compose.jsp page

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
    <title>Compose</title>
    <link rel="stylesheet" type="text/css" href="<c:url value="/css/style.css"/>">
</head>
<body>
    <form action="send" method="post">
        <h1>Compose
            <span>Write your email</span>
        </h1>
        <label>
            <span>To :</span>
            <input type="text" name="to" 
                placeholder="Email of recipient" required/>
        </label>
        <label>
            <span>Subject :</span>
            <input type="text" name="subject" 
                placeholder="Subject"/>
        </label>
        <label>
            <span>Text :</span>
            <textarea name="text" 
                      placeholder="Message"></textarea>
        </label>
        <label>
            <span>&nbsp;</span> 
            <input type="submit" value="Send"/> 
        </label>    
    </form>
</body>
</html>

Finally, the Error.jsp page in Listing 16.23 shows any error message that occurred.

Listing 16.23: The Error.jsp page

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
    <title>Error</title>
    <link rel="stylesheet" type="text/css" href="<c:url value="/css/style.css"/>">
</head>
<body>
    ${error}
</body>
</html>

Running the Application

If you are using NetBeans, then you just need to right click on the project icon and click Run. If you not using NetBeans, deploy the application to the running servlet container and direct your browser to this URL:

http://localhost:8080/javawebmail/

Summary

You use the SMTP protocol to send emails and POP3 or IMAP to store and receive emails. JavaMail provides the functionality for working with these protocols and shield you from the complexity of these protocol.

In this chapter you learned how to use JavaMail and Apache Commons Email to send and receive emails. You also learned how to build a Model 2 application that allows access to your email via a web interface.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.78.106