Skip to content

Sending Email

Email is electronic mail. The application of email has a history of several decades. We are familiar with email addresses like abc@example.com and email software like Outlook, which are used to send and receive emails.

Java programs can also send and receive emails. Let’s first look at how traditional emails are sent.

How Traditional Emails Are Sent

Traditional emails are delivered through a postal service, moving from one post office to another, and finally reaching the user's mailbox:

           ┌──────────┐    ┌──────────┐
           │PostOffice│    │PostOffice│     .───.
┌─────┐    ├──────────┤    ├──────────┤    (   ( )
│═══ ░│───▶│ ┌─┐ ┌┐┌┐ │───▶│ ┌─┐ ┌┐┌┐ │───▶ `─┬─'
└─────┘    │ │░│ └┘└┘ │    │ │░│ └┘└┘ │       │
           └─┴─┴──────┘    └─┴─┴──────┘       │

The process of sending an electronic email is similar, except that an email is sent from the user's computer email software, such as Outlook, to an email server. It may pass through several email servers before reaching the recipient's email server. The recipient can then use software or log in through a browser to view the email:

             ┌─────────┐    ┌─────────┐    ┌─────────┐
             │░░░░░░░░░│    │░░░░░░░░░│    │░░░░░░░░░│
┌───────┐    ├─────────┤    ├─────────┤    ├─────────┤    ┌───────┐
│░░░░░░░│    │░░░░░░░░░│    │░░░░░░░░░│    │░░░░░░░░░│    │░░░░░░░│
├───────┤    ├─────────┤    ├─────────┤    ├─────────┤    ├───────┤
│       │───▶│O ░░░░░░░│───▶│O ░░░░░░░│───▶│O ░░░░░░░│◀───│       │
└───────┘    └─────────┘    └─────────┘    └─────────┘    └───────┘
   MUA           MTA            MTA            MDA           MUA

We refer to email software like Outlook as MUA: Mail User Agent, meaning a mail agent that serves the user. Email servers are referred to as MTA: Mail Transfer Agent, meaning mail transfer agents. The final email server is called MDA: Mail Delivery Agent, meaning mail delivery agents. Once an email reaches the MDA, it no longer moves. In reality, emails are usually stored on the MDA server's hard drive, waiting for the recipient to view them through software or by logging into a browser.

SMTP Protocol

Server software like MTA and MDA are typically ready-made, and we do not concern ourselves with how these servers operate internally. To send an email, we focus on writing an MUA software that sends the email to an MTA.

The protocol used by the MUA to send emails to the MTA is the SMTP protocol, which stands for Simple Mail Transfer Protocol. It uses the standard port 25, or the encrypted ports 465 or 587.

SMTP is a protocol built on top of TCP. Any program that sends emails must adhere to the SMTP protocol. When using Java to send emails, we do not need to worry about the underlying principles of SMTP. We can simply use the JavaMail standard API to send emails directly.

Preparing SMTP Login Information

Suppose we plan to use our email address me@example.com to send an email to Xiaoming, whose email address is xiaoming@somewhere.com. Before sending the email, we need to determine the SMTP server address and port number. The SMTP server address is usually smtp.example.com, and the port number is determined by the email service provider, typically using ports 25, 465, or 587. Below are SMTP details for some common email service providers:

  • QQ Mail: SMTP server is smtp.qq.com, ports 465/587;
  • 163 Mail: SMTP server is smtp.163.com, port 465;
  • Gmail: SMTP server is smtp.gmail.com, ports 465/587.

With the SMTP server domain name and port number, we also need the SMTP server's login information, which typically uses your email address as the username and your password or a separately set SMTP password as the login credential.

Let’s see how to send an email using JavaMail.

Using JavaMail to Send Emails

First, we need to create a Maven project and add the following two JavaMail dependencies:

xml
<dependencies>
    <dependency>
        <groupId>jakarta.mail</groupId>
        <artifactId>javax.mail-api</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>jakarta.mail</artifactId>
        <version>2.0.1</version>
    </dependency>
</dependencies>

These two packages include one for interface definitions and one for concrete implementations. If using the earlier 1.x version, note that the package names differ:

xml
<dependencies>
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>javax.mail-api</artifactId>
        <version>1.6.2</version>
    </dependency>
    <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>javax.mail</artifactId>
        <version>1.6.2</version>
    </dependency>
</dependencies>

Additionally, replace jakarta.mail with javax.mail in your code references.

Next, connect to the SMTP server using the JavaMail API:

java
// Server address:
String smtp = "smtp.office365.com";
// Login username:
String username = "jxsmtp101@outlook.com";
// Login password:
String password = "********";
// Connect to SMTP server on port 587:
Properties props = new Properties();
props.put("mail.smtp.host", smtp); // SMTP host name
props.put("mail.smtp.port", "587"); // Host port number
props.put("mail.smtp.auth", "true"); // Whether user authentication is needed
props.put("mail.smtp.starttls.enable", "true"); // Enable TLS encryption
// Get Session instance:
Session session = Session.getInstance(props, new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(username, password);
    }
});
// Enable debug mode for easier debugging:
session.setDebug(true);

For port 587, when connecting to the SMTP server, you need to prepare a Properties object with the relevant information. Finally, when obtaining the Session instance, if the server requires authentication, you need to pass an Authenticator object that returns the specified username and password.

Once you have the Session instance, enabling debug mode allows you to see detailed SMTP communication, facilitating debugging.

Sending an Email

To send an email, we need to construct a Message object and then call Transport.send(Message) to complete the sending process:

java
MimeMessage message = new MimeMessage(session);
// Set the sender's address:
message.setFrom(new InternetAddress("me@example.com"));
// Set the recipient's address:
message.setRecipient(Message.RecipientType.TO, new InternetAddress("xiaoming@somewhere.com"));
// Set the email subject:
message.setSubject("Hello", "UTF-8");
// Set the email body:
message.setText("Hi Xiaoming...", "UTF-8");
// Send the email:
Transport.send(message);

Most email servers require that the sender's address matches the login username; otherwise, the sending will fail.

After filling in the real addresses and running the above code, you can see the debug information printed by JavaMail in the console:

This is the debug information printed by JavaMail:
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: need username and password for authentication
DEBUG SMTP: protocolConnect returning false, host=smtp.office365.com, ...
DEBUG SMTP: useEhlo true, useAuth true
Attempting to connect to smtp.office365.com:
DEBUG SMTP: trying to connect to host "smtp.office365.com", port 587, ...
DEBUG SMTP: connected to host "smtp.office365.com", port: 587
Sending EHLO command:
EHLO localhost
SMTP server response 250:
250-SG3P274CA0024.outlook.office365.com Hello
250-SIZE 157286400
...
DEBUG SMTP: Found extension "SIZE", arg "157286400"
Sending STARTTLS command:
STARTTLS
SMTP server response 220:
220 2.0.0 SMTP server ready
EHLO localhost
250-SG3P274CA0024.outlook.office365.com Hello [111.196.164.63]
250-SIZE 157286400
250-PIPELINING
250-...
DEBUG SMTP: Found extension "SIZE", arg "157286400"
...
Attempting to log in:
DEBUG SMTP: protocolConnect login, host=smtp.office365.com, user=********, password=********
DEBUG SMTP: Attempt to authenticate using mechanisms: LOGIN PLAIN DIGEST-MD5 NTLM XOAUTH2 
DEBUG SMTP: Using mechanism LOGIN
DEBUG SMTP: AUTH LOGIN command trace suppressed
Login successful:
DEBUG SMTP: AUTH LOGIN succeeded
DEBUG SMTP: use8bit false
Sending email, setting FROM:
MAIL FROM:<********@outlook.com>
250 2.1.0 Sender OK
Setting TO:
RCPT TO:<********@sina.com>
250 2.1.5 Recipient OK
Sending email data:
DATA
SMTP server response 354:
354 Start mail input; end with <CRLF>.<CRLF>
Actual email data:
Date: Mon, 2 Dec 2019 09:37:52 +0800 (CST)
From: ********@outlook.com
To: ********001@sina.com
Message-ID: <1617791695.0.1575250672483@localhost>
Subject is encoded text:
Subject: =?UTF-8?Q?JavaMail=E9=82=AE=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64

Email body is Base64 encoded text:
SGVsbG8sIOi/meaYr+S4gOWwgeadpeiHqmphdmFtYWls55qE6YKu5Lu277yB
.
Email data sending completed with `\r\n.\r\n`, server response 250 indicates successful sending:
250 2.0.0 OK <HK0PR03MB4961.apcprd03.prod.outlook.com> [Hostname=HK0PR03MB4961.apcprd03.prod.outlook.com]
DEBUG SMTP: message successfully delivered to mail server
Sending QUIT command:
QUIT
SMTP server response 221 to end the TCP connection:
221 2.0.0 Service closing transmission channel

From the above debug information, we can see that SMTP is a request-response protocol. The client always sends commands and then waits for the server's response. Server responses always start with a number, and the subsequent information is text used for debugging. These response codes are defined in the SMTP protocol. By checking specific response codes, you can identify the cause of errors.

Sending HTML Emails

Sending HTML emails is similar to sending plain text emails. You only need to change:

java
message.setText(body, "UTF-8");

to:

java
message.setText(body, "UTF-8", "html");

The body parameter should be an HTML string like <h1>Hello</h1><p>Hi, xxx</p>.

HTML emails can be displayed as web pages directly in email clients:

Sending Emails with Attachments

To include attachments in an email, you cannot directly call the message.setText() method. Instead, you need to construct a Multipart object:

java
Multipart multipart = new MimeMultipart();
// Add text part:
BodyPart textpart = new MimeBodyPart();
textpart.setContent(body, "text/html;charset=utf-8");
multipart.addBodyPart(textpart);
// Add image part:
BodyPart imagepart = new MimeBodyPart();
imagepart.setFileName(fileName);
imagepart.setDataHandler(new DataHandler(new ByteArrayDataSource(input, "application/octet-stream")));
multipart.addBodyPart(imagepart);
// Set the email content to multipart:
message.setContent(multipart);

A Multipart object can contain several BodyPart objects. The first BodyPart is the text, i.e., the email body, and the subsequent BodyPart objects are attachments. The content added to a BodyPart depends on the setContent() method. If adding text, use setContent("...", "text/plain;charset=utf-8") for plain text or setContent("...", "text/html;charset=utf-8") for HTML text. If adding an attachment, you need to set the file name (which does not necessarily have to match the real file name) and add a DataHandler() with the file's MIME type. Binary files can use application/octet-stream, and Word documents can use application/msword.

Finally, by using setContent() to add the Multipart to the Message, the email can be sent.

Emails with attachments will prompt the recipient to download the attachments:

Sending HTML Emails with Embedded Images

Some may have noticed that HTML emails can embed images. How is this achieved?

If you use <img src="http://example.com/test.jpg"> in an HTML email, external image links are usually filtered by email clients and prompt the user that displaying images may not be safe. Only embedded images can be displayed normally in the email.

Embedded images are actually attachments. The email itself is a Multipart, but with some additional processing:

java
Multipart multipart = new MimeMultipart();
// Add text part:
BodyPart textpart = new MimeBodyPart();
textpart.setContent("<h1>Hello</h1><p><img src=\"cid:img01\"></p>", "text/html;charset=utf-8");
multipart.addBodyPart(textpart);
// Add image part:
BodyPart imagepart = new MimeBodyPart();
imagepart.setFileName(fileName);
imagepart.setDataHandler(new DataHandler(new ByteArrayDataSource(input, "image/jpeg")));
// Associate with the HTML's <img src="cid:img01">:
imagepart.setHeader("Content-ID", "<img01>");
multipart.addBodyPart(imagepart);

When referencing an image in an HTML email, you need to set an ID, such as <img src="cid:img01">. Then, when adding the image as a BodyPart, in addition to correctly setting the MIME type (e.g., image/jpeg or image/png based on the image type), you also need to set a header:

java
imagepart.setHeader("Content-ID", "<img01>");

This ID corresponds to the ID referenced in the HTML, allowing the email client to display the embedded image correctly:

Common Issues

  • Incorrect Username or Password: This will cause a 535 Authentication Failed error.

    DEBUG SMTP: AUTH LOGIN failed
    Exception in thread "main" javax.mail.AuthenticationFailedException: 535 5.7.3 Authentication unsuccessful [HK0PR03CA0105.apcprd03.prod.outlook.com]
  • Sender and Login User Mismatch: This will cause a 554 Send As Denied error.

    DEBUG SMTP: MessagingException while sending, THROW: 
    com.sun.mail.smtp.SMTPSendFailedException: 554 5.2.0 STOREDRV.Submission.Exception:SendAsDeniedException.MapiExceptionSendAsDenied;
  • Simple Email Subject or Body: If the email subject and body are too simple, it may be flagged as spam, resulting in a 554 Spam Detected error.

    DEBUG SMTP: MessagingException while sending, THROW: 
    com.sun.mail.smtp.SMTPSendFailedException: 554 DT:SPM

In summary, when errors occur, you need to review the DEBUG information to find the server's error codes and descriptions to identify the cause of the error.

Exercise

Use SMTP to send an email.

Summary

  • Using the JavaMail API to send emails is essentially creating an MUA (Mail User Agent) software that sends emails to an MTA (Mail Transfer Agent) server via the SMTP protocol.
  • Enabling debug mode allows you to see detailed SMTP interaction information.
  • Some email service providers require enabling SMTP and setting up a separate SMTP password.
Sending Email has loaded