Jakarta Mail Guide: Configuration, SMTP Setup, and Email Sending

On October 17, 2024
17min read
Ivan Djuric, an author at Mailtrap
Ivan Djuric Technical Content Writer @Mailtrap

In this article, I break down my sending flow with Jakarta Mail API, from plain-text emails to asynchronous and bulk emails.

However, first I’ll show you how to set up Jakarta Mail if you’re starting fresh, and I’ll also go over migrating an existing project from Java Mail. 

P.S. To skip theory and jump straight to sending, simply click here.

Set up Jakarta Mail

To start using Jakarta Mail, you need to download the latest version of Jakarta Mail from the official GitHub page. As of writing this article, it’s Jakarta Mail 2.1.3., so all code snippets I provide are compatible with that version.

Or, what I recommend, use dependency managers like Maven or Gradle:

  • Maven

Generate a pom.xml file by running the following command:

mvn archetype:generate

Then, paste the following dependency inside pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.mailtrap</groupId>
    <artifactId>java-email-mvn</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.eclipse.angus</groupId>
            <artifactId>angus-mail</artifactId>
            <version>2.0.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.4.1</version>
                <configuration>
                    <mainClass>org.mailtrap.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • Gradle

Generate a basic Gradle project structure with:

gradle init

Along with the necessary project structure, the provided command will create the build.gradle file in your root directory. Inside the build.gradle, paste the following lines:

plugins {
    id 'java'
    id 'application'
}

group = 'org.mailtrap'
version = '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

application {
    mainClass = 'Main' // Replace 'Main' with your main class name
}

dependencies {
    implementation 'org.eclipse.angus:angus-mail:2.0.3' // Latest version of Angus Mail
}

JavaMail to Jakarta Mail migration

With the release of Jakarta EE 8 in 2019, JavaMail officially migrated to Jakarta Mail. The migration was part of the Java EE (Enterprise Edition) ecosystem to Jakarta EE platform, which is now managed by the Eclipse Foundation.

And with the change in ownership, there comes change in code structure. Don’t worry though, I’ve got your back.

To avoid this mess and successfully migrate an existing project from Java Mail to Jakarta Mail, simply do the following:

1. Update dependencies 

First, you’ll need to update the dependencies, which, if you didn’t use Maven or Gradle to install Jakarta, you must do manually by including the jakarta.mail.jar file in your CLASSPATH, like so:

# for Windows
set CLASSPATH=%CLASSPATH%;c:\path\to\jakarta.mail.jar;.

# for Linux/macOS
export CLASSPATH=$CLASSPATH:/path/to/jakarta.mail.jar:.

2.  Update your imports

javax.mail imports must be updated to jakarta.mail.

You need to do this across the entire project where the old javax.mail classes are referenced.

For example:

# From:

import javax.mail.*;
import javax.mail.internet.*;

# To:

import jakarta.mail.*;
import jakarta.mail.internet.*;

Although you update the imports line by line, I recommend using the Find and Replace option most code editors have. For example, here it is in IntelliJ IDEA:

3. (Optional) Use Gradle-specific solutions

Feeling tech savvy or just a fan of Gradle? Here are two articles I’ve found super useful when setting up my Java after the switch from Java to Jakarta mail happened:

Luckily, beyond the package changes I wrote about above, the majority of the code remains the same.

Send email using Jakarta Mail and SMTP

Now, all there is to do is to configure SMTP or mail server settings. Personally, I use Mailtrap SMTP, a versatile solution with robust email sending capabilities that ensures my emails reach recipients’ inboxes by design.  

Here’s how it works:

  • Create a free account
  • Verify your email-sending domain
  • Navigate to Sending Domains and choose the domain you verified 
  • Click on Integration and choose the Transactional Stream for now (more on the Bulk Stream a bit later).

Then, all there’s left to do is copy the credentials into your Main.java file. As I know Java’s project structure can be difficult to navigate, here’s where my Main.Java file was located for example:

And here’s the code you can use to start sending plain-text emails:

package org.mailtrap;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.util.Properties;

public class Main {
    public static void main(String[] args) {

        // provide recipient's email ID
        String to = "your.recipient@email.com";
        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username
        final String username = "api";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "live.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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");

        // create the Session object
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });

        try {
            // create a MimeMessage object
            Message message = new MimeMessage(session);
            // set From email field
            message.setFrom(new InternetAddress(from));
            // set To email field
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
            // set email subject field
            message.setSubject("Hello from the Mailtrap team");
            // set the content of the email message
            message.setText("Enjoy sending emails from Jakarta Mail!");

            // send the email message
            Transport.send(message);

            System.out.println("Email Message Sent Successfully!");

        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}

If you’ve manually resolved dependencies, simply run Main.java.

But if you’ve used Maven or Gradle for dependencies, you’ll need to run the following commands first to compile and resolve the dependencies. Think of it as cleaning your car before going out for a drive. Here’s what you need to run:

# Maven
mvn clean compile
mvn exec:java

# Gradle
./gradlew clean build

# Gradle on Windows
gradlew.bat clean build

Send HTML email

To send HTML emails in Jakarta, all you have to do is:

  • Replace the setText() with the setContent() method.
  • Place text/html as the second argument.
  • Add the String htmlContent line where you’ll add your HTML content.

And here’s the code for you to dissect:

// create a MimeMessage object
Message message = new MimeMessage(session);
// set From email field
message.setFrom(new InternetAddress(from));
// set To email field
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
// set email subject field
message.setSubject("Hello from the Mailtrap team!");

// set the content of the email message to HTML
String htmlContent = "<h1>Welcome to Jakarta Mail!</h1><p>This is an HTML email!</p>";
message.setContent(htmlContent, "text/html"); // Specify the content type as HTML

// send the email message
Transport.send(message);

As per best email-sending practices, I advise you also keep plain-text along with your HTML content in case some of your recipients’ browser/clients doesn’t render it. To do this, you can use the MimeMultipart(“alternative”) object, like so:

// create a MimeMessage object
Message message = new MimeMessage(session);
// set From email field
message.setFrom(new InternetAddress(from));
// set To email field
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
// set email subject field
message.setSubject("Hello from the Mailtrap Team!");

// create the multipart message
Multipart multipart = new MimeMultipart();

// create the plain-text part
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText("Just discovered that Jakarta Mail is fun and easy to use!");

// create the HTML part
MimeBodyPart htmlPart = new MimeBodyPart();
String htmlContent = "<h1>Welcome to Jakarta Mail!</h1><p>This is an HTML email!</p>";
htmlPart.setContent(htmlContent, "text/html");

// add both parts to the multipart message
multipart.addBodyPart(textPart);
multipart.addBodyPart(htmlPart);

// set the content of the message to the multipart
message.setContent(multipart);

// send the email message
Transport.send(message);

For more details and pro tips, check out our in-depth article on sending HTML in Java.

Send email to multiple recipients

To send emails to multiple recipients in Jakarta, you need to: 

  • Use the setRecipients() method instead of setRecipient(), which will allow you to specify a list of addresses.
  • Add multiple email addresses as a comma-separated string with InternetAddress.parse() splitting them.

Check it out in action:

package org.mailtrap;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.util.Properties;

public class Main {
    public static void main(String[] args) {

        // provide recipients' email IDs
        String to = "your.recipient@email.com, your.recipient2@email.com, your.recipient3@email.com";
        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username
        final String username = "api";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "live.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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");

        // create the Session object
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });

        try {
            // create a MimeMessage object
            Message message = new MimeMessage(session);
            // set From email field
            message.setFrom(new InternetAddress(from));
            // set multiple To email addresses using parse
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
            // set email subject field
            message.setSubject("Hello from the Mailtrap team");
            // set the content of the email message
            message.setText("Just discovered that Jakarta Mail is fun and easy to use!");

            // send the email message
            Transport.send(message);

            System.out.println("Email Message Sent Successfully!");

        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}

Note: This code sends the email to all recipients you specify in the to string. However, if you want to send to CC and BCC recipients, below the message.SetRecipient() add either:

Message.RecipientType.CC

# or 

Message.RecipientType.BCC

# or both 🙂

Send email with attachments

Create an instance of the MimeMultipart object that will be used for wrapping the MimeBodyPart body parts. A MimeMultipart acts like a container that holds multiple body parts, and it comes with methods for getting and setting its various subparts. 

  • Set the first part of the MimeMultipart object by passing the actual message to it. 
  • Set the second part of the MimeMultipart object by adding an attachment using a DataHandler object.
  • Include the MimeMultipart in the message to be sent. 

Here is what the steps look like when implemented in code:

package org.mailtrap;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;

import java.io.File;
import java.util.Properties;

public class Main {
    public static void main(String[] args) {

        // provide recipient's email ID
        String to = "your.recipient@email.com";
        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username
        final String username = "api";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "live.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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");

        // create the Session object
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });

        try {
            // create a MimeMessage object
            Message message = new MimeMessage(session);
            // set From email field
            message.setFrom(new InternetAddress(from));
            // set To email field
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
            // set email subject field
            message.setSubject("Hello from the Mailtrap team");

            // create the message body part
            MimeBodyPart messageBodyPart = new MimeBodyPart();
            messageBodyPart.setText("Just discovered that Jakarta Mail is fun and easy to use!");

            // create a second MimeBodyPart to hold the attachment
            MimeBodyPart attachmentPart = new MimeBodyPart();
            String filename = "/path/to/your/file.txt"; // Specify the file path
            attachmentPart.attachFile(new File(filename));

            // create a Multipart object to combine the message body and the attachment
            Multipart multipart = new MimeMultipart();
            multipart.addBodyPart(messageBodyPart);  // Add message body
            multipart.addBodyPart(attachmentPart);   // Add attachment

            // set the content of the email to the Multipart object
            message.setContent(multipart);

            // send the email message
            Transport.send(message);

            System.out.println("Email Message Sent Successfully with Attachment!");

        } catch (MessagingException | java.io.IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Code breakdown: The MimeMultipart object will combine the email’s body and the attachment, whereas Transport.send() will send the message with the attachment. Just make sure that the filename variable contains the correct file path.

Send email with embedded image

To send emails with embedded images in Jakarta, we will create: 

  • A MimeMultipart to hold HTML content and the image together.
  • A MimeBodyPart for the HTML content and the image reference.
  • Another MimeBodyPart for the image itself.

Then, to seal the deal, we need to set a Content-ID header to reference the image in the HTML, set the Multipart as the content of the message, and finally send the email.

Sounds complicated? Don’t worry, I got you. Here’s code you can use:

package org.mailtrap;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;

import java.io.File;
import java.util.Properties;

public class Main {
    public static void main(String[] args) {

        // provide recipient's email ID
        String to = "your.recipient@email.com";
        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username
        final String username = "api";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "live.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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");

        // create the Session object
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });

        try {
            // create a MimeMessage object
            Message message = new MimeMessage(session);
            // set From email field
            message.setFrom(new InternetAddress(from));
            // set To email field
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
            // set email subject field
            message.setSubject("Here comes Jakarta Mail with an embedded image!");

            // Create the MimeMultipart object to hold the email content (HTML + image)
            MimeMultipart multipart = new MimeMultipart("related");

            // First part: the HTML content with an embedded image reference
            MimeBodyPart htmlPart = new MimeBodyPart();
            String htmlContent = "<h1>Welcome to Jakarta Mail!</h1>"
                    + "<p>This is an HTML email with an embedded image.</p>"
                    + "<img src='cid:image_id'>";
            htmlPart.setContent(htmlContent, "text/html");

            // Second part: the image
            MimeBodyPart imagePart = new MimeBodyPart();
            String imagePath = "/path/to/your/image.jpg";  // Update with the actual path
            imagePart.attachFile(new File(imagePath));
            imagePart.setHeader("Content-ID", "<image_id>");  // Image identifier used in HTML

            // Add both parts (HTML and image) to the multipart
            multipart.addBodyPart(htmlPart);
            multipart.addBodyPart(imagePart);

            // Set the content of the message to the multipart object
            message.setContent(multipart);

            // send the email message
            Transport.send(message);

            System.out.println("Email Message Sent Successfully with Embedded Image!");

        } catch (MessagingException | java.io.IOException e) {
            throw new RuntimeException(e);
        }
    }
}

With this code, the image will be referenced in the HTML content and displayed within the email body, instead of being sent as an attachment. And of course, make sure to replace the imagePath with the actual file path of the image you want to include.

Asynchronous email sending

As Jakarta Mail can’t send emails asynchronously on its own, we can use CompletableFuture, a part of Java Concurrency API that allows our app to continue in a separate thread without waiting for our email-sending process to complete.

And here’s how you can implement it:

import java.util.concurrent.CompletableFuture;
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.Properties;

public class Main {
    public static void main(String[] args) {

        // provide recipient's email ID
        String to = "your.recipient@email.com";
        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username
        final String username = "api";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "live.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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");

        // Run the email task asynchronously
        CompletableFuture.runAsync(() -> {


try {
                // create the Session object
                Session session = Session.getInstance(props, new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(username, password);
                    }
                });

                // create a MimeMessage object
                Message message = new MimeMessage(session);
                // set From email field
                message.setFrom(new InternetAddress(from));
                // set To email field
                message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
                // set email subject field
                message.setSubject("Hello from the Mailtrap team!");
                // set the content of the email message
                message.setText("Just discovered that Jakarta Mail is fun and easy to use!");

                // send the email message
                Transport.send(message);

                System.out.println("Email Message Sent Successfully!");

            } catch (MessagingException e) {
                e.printStackTrace();
            }
        }).exceptionally(ex -> {
            System.out.println("Failed to send email: " + ex.getMessage());
            return null;
        });

        System.out.println("Email task submitted for asynchronous execution!");
    }
}

Code details

  • CompletableFuture.runAsync() allows the email-sending process to run on a separate thread.
  • exceptionally() catches any exceptions like MessagingException.

Send bulk email

Although CompletableFuture is great for simple asynchronous tasks, it isn’t designed for high-volume email-sending.

To send bulk email, we’ll use ExecutorService, which gives you finer control over thread pools. 

Here’s an example:

package org.mailtrap;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {

        // List of recipient email IDs (you can expand this as needed)
        List<String> recipientList = List.of("recipient1@example.com", "recipient2@example.com", "recipient3@example.com", "recipient4@example.com", "recipient5@example.com");

        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username and password
        final String username = "api";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "bulk.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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");

        // Create a batch size and delay for rate limiting
        int batchSize = 2; // Number of emails to send per batch
        long delayBetweenBatches = 10_000; // Delay in milliseconds (10 seconds)

        // Create batches from the recipient list
        List<List<String>> batches = createBatches(recipientList, batchSize);

        // create a fixed thread pool executor for bulk email sending
        ExecutorService executor = Executors.newFixedThreadPool(5); // 5 threads for sending emails concurrently

        // Iterate over each batch
        for (List<String> batch : batches) {
            // Submit tasks for each email in the batch
            for (String to : batch) {
                executor.submit(() -> sendEmail(to, from, props, username, password));
            }

            // Introduce a delay between batches for rate limiting
            try {
                System.out.println("Waiting for " + delayBetweenBatches / 1000 + " seconds before sending the next batch...");
                Thread.sleep(delayBetweenBatches);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();  // Handle the interruption properly
                System.out.println("Batch processing interrupted");
            }
        }

        // Shutdown the executor service gracefully after all tasks are submitted
        executor.shutdown();
    }

    // Method to send individual email
    public static void sendEmail(String to, String from, Properties props, final String username, final String password) {
        // create the Session object
        Session session = Session.getInstance(props, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password);
            }
        });

        try {
            // create a MimeMessage object
            Message message = new MimeMessage(session);
            // set From email field
            message.setFrom(new InternetAddress(from));
            // set To email field
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
            // set email subject field
            message.setSubject("Hello from the Mailtrap team!");
            // set the content of the email message
            message.setText("Just discovered that Jakarta Mail is fun and easy to use!");

            // send the email message
            Transport.send(message);

            System.out.println("Email sent to: " + to);

        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    // Helper method to create batches from the recipient list
    public static List<List<String>> createBatches(List<String> recipientList, int batchSize) {
        List<List<String>> batches = new ArrayList<>();
        int totalSize = recipientList.size();

        for (int i = 0; i < totalSize; i += batchSize) {
            int end = Math.min(i + batchSize, totalSize);
            batches.add(recipientList.subList(i, end));
        }

        return batches;
    }
}

Code breakdown:

  • ExecutorService.newFixedThreadPool(5) creates a 5-pool thread, but you can adjust it according to how many emails you want to send.
  • An email task is submitted to the executor after the code loops over recipientList, to which you can add as many recipients you need.
  • Once the tasks are submitted, executor.shutdown() makes the app wait for them to complete before it shuts down.
  • I’ve also added batching (createBatches())and rate limiting (Thread.sleep()) to make sure you don’t overwhelm your SMTP server or get flagged as spam by email services.

Most importantly, as you can notice from the credentials in the code, I’ve used bulk.smtp.mailtrap.io. This is the host name of Mailtrap Bulk Stream I mentioned a bit earlier in the article, which allows me to send large amounts of emails while keeping my deliverability high.

Debug Jakarta Mail

In Jakarta Mail, debugging is pretty straightforward and you just have to set debug to ‘true’ in the properties of your email code:

props.put("mail.debug", "true");

As a result, you will get a step by step description of how your code is executed. If any problem with sending your email appears, you will instantly understand what happened and at which stage.

Here is how our HTML message debug output looks:

DEBUG: Jakarta Mail version 2.0.1
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPTransport=jakarta.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=jakarta.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3Store=jakarta.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], com.sun.mail.smtp.SMTPSSLTransport=jakarta.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.imap.IMAPStore=jakarta.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3SSLStore=jakarta.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle]}
DEBUG: Providers Listed By Protocol: {imap=jakarta.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtp=jakarta.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], pop3=jakarta.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], imaps=jakarta.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], smtps=jakarta.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3s=jakarta.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: getProvider() returning jakarta.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: need username and password for authentication
DEBUG SMTP: protocolConnect returning false, host=send.smtp.mailtrap.io, user=diana, password=<null>
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "live.smtp.mailtrap.io", port 587, isSSL false
220 mailtrap.io ESMTP
DEBUG SMTP: connected to host "live.smtp.mailtrap.io", port: 587
EHLO localhost
250-mailtrap.io
250-PIPELINING
250-SIZE 10485760
250-STARTTLS
250-ENHANCEDSTATUSCODES
250 8BITMIME
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "SIZE", arg "10485760"
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
STARTTLS
220 2.0.0 Ready to start TLS
EHLO localhost
250-mailtrap.io
250-PIPELINING
250-SIZE 10485760
250-AUTH LOGIN PLAIN
250-ENHANCEDSTATUSCODES
250 8BITMIME
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "SIZE", arg "10485760"
DEBUG SMTP: Found extension "AUTH", arg "LOGIN PLAIN"
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: protocolConnect login, host=send.smtp.mailtrap.io, user=api, password=<non-null>
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
DEBUG SMTP: AUTH LOGIN succeeded
DEBUG SMTP: use8bit false
MAIL FROM:<yourmail@example.com>>
250 2.1.0 Ok
RCPT TO:<johndoe@gmail.com>
250 2.1.5 Ok
DEBUG SMTP: Verified Addresses
DEBUG SMTP:   johndoe@gmail.com
DATA
354 End data with <CR><LF>.<CR><LF>
Date: Mon, 16 Jan 2024 14:14:45 +0100 (CET)
From: yourmail@example.com
To: johndoe@gmail.com
Message-ID: <380936215.0.1673874885717@localhost>
Subject: Hello from the Mailtrap team!
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Just discovered that Jakarta Mail is fun and easy to use
.
250 2.0.0 Ok: queued as c0a683fa-959f-11ed-b75e-0a58a9feac02
DEBUG SMTP: message successfully delivered to mail server
QUIT
221 2.0.0 Bye
Email Message Sent Successfully

Test emails and email sending on staging

Besides debugging, another crucial step in every solid email-sending cycle is testing your emails. Why? Well, have you ever received an email that’s not responsive, has broken links, and/or blurry images? Or an email that, in general, looks like no one previewed it before sending it? 🤔

If you are anything like me, you probably felt second-hand embarrassment for the sender. Yet, all the sender had to do was run some tests on their email to prevent that. 

And that’s exactly where email testing, an industry-standard practice, comes in!

Personally, I inspect and debug my emails with Mailtrap Email Testing, another inseparable part of Mailtrap Email Delivery Platform that provides devs and QAs a safe sandbox environment to test emails without the risk of sending them to recipients.

Mailtrap Email Testing comes with a plethora of advanced features, such as:

  • Email preview – Check out how your HTML emails look before you send them out. Or, preview them in source HTML, text, raw, and more.
  • Inspect your HTML/CSS – Inspect the HTML/CSS of your emails and easily spot any faulty lines of code. By using this feature, you can ensure your emails are spotless and can be rendered by any browser/email client.
  • Spam Analysis – Get a full SPAM report, domain blacklisting details, and a spam score, which, if you keep under 5, significantly reduces your odds of hitting spam filters and ending up in recipients’ junk folders.

Most importantly, Email Testing is quite straightforward to use. To start testing, all you have to do is:

  • Create a free Mailtrap account
  • Navigate to Email Testing and choose your inbox
  • Find your credentials from the Integration tab

Then, simply copy the provided fake SMTP credentials into your Jakarta Mail configuration. Here’s how it should look like:

package org.mailtrap;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.util.Properties;

public class Main {
    public static void main(String[] args) {

        // provide recipient's email ID
        String to = "your.recipient@email.com";
        // provide sender's email ID
        String from = "john.doe@your.domain";

        // provide Mailtrap's username
        final String username = "994a047d0fb931";
        final String password = "9c8aee72e7e4ebdd64b57bf2df163e1e";

        // provide Mailtrap's host address
        String host = "sandbox.smtp.mailtrap.io";

        // configure Mailtrap's SMTP details
        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"); // recommended, other options: 2525 or 25

        // create the Session object
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });

        try {
            // create a MimeMessage object
            Message message = new MimeMessage(session);
            // set From email field
            message.setFrom(new InternetAddress(from));
            // set To email field
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
            // set email subject field
            message.setSubject("This is a test email!");
            // set the content of the email message
            message.setText("Everything works correctly? Let’s go!");

            // send the email message
            Transport.send(message);

            System.out.println("Email Message Sent Successfully!");

        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}

Run the code, and the first test email should land in your Mailtrap Email Testing virtual inbox very shortly.

And that is it! You can now enjoy the above-mentioned features as well as benefits such as automation, saving time, and preserving domain reputation.

Wrapping up

And folks, that’s how you can use the Jakarta Mail API and add powerful emailing capabilities to your Java applications. 

Do keep in mind that here we’ve just scratched the surface of what is possible with the Jakarta Mail. So, if you want to explore more usage examples, you can always check its expansive documentation.

Enjoy using Jakarta Mail! 📧

Ivan Djuric, an author at Mailtrap
Article by Ivan Djuric Technical Content Writer @Mailtrap

I’m a Technical Content Writer with 5 years of background covering email-related topics in tight collaboration with software engineers and email marketers. I just love to research and share actionable insights with you about email sending, testing, deliverability improvements, and more. Happy to be your guide in the world of emails!