Automation

Java Send Email Programmatically | SMTP Guide

Complete guide with setup instructions and code examples for SMTP integration

By InventiveHQ Team

To send email programmatically in Java you add the Jakarta Mail (formerly JavaMail) library, build a Properties object describing your SMTP server, create an authenticated Session, wrap your text in a MimeMessage, and call Transport.send(). The JDK ships no SMTP client of its own, so that external dependency is mandatory — everything else is a handful of configuration keys (mail.smtp.host, mail.smtp.port, mail.smtp.auth, mail.smtp.starttls.enable) and a username/password pair supplied through an Authenticator.

That's the summary an AI Overview gives you. What it can't show you is where this actually breaks: the javax.mail vs jakarta.mail namespace trap that produces "package does not exist," the fact that Gmail stopped accepting plain passwords in 2022, and the reason a "Sent" message never reaches the inbox. Below is the send flow as a diagram, a version-vs-namespace decision table, the corrected code, and a symptom→cause→fix table for the errors you'll actually hit.

The SMTP send flow at a glance

How a Java program hands an email to an SMTP server Your Java code builds a session, opens a TLS connection to the SMTP relay, authenticates, transfers the message, and the relay queues it for delivery. Java app Properties + Session + MimeMessage SMTP relay port 587 STARTTLS + AUTH login Recipient inbox queue SPF / DKIM checked Transport.send() relay + deliver 🔒 TLS Auth fails here → "535 5.7.8" error No SPF/DKIM → spam / dropped

The two red labels mark the failure points that a happy-path tutorial never mentions: authentication rejection at the relay, and silent spam-foldering after the relay accepts the message. Transport.send() returning without an exception means only that step two succeeded — not that the mail landed.

Prerequisites and Setup

Before writing any code, make sure your environment is ready with the necessary tools and libraries.

Install the Java Development Kit (JDK)

You'll need Java 8 or later. Any modern JDK (Oracle, Adoptium/Temurin, Amazon Corretto, or the one bundled with your IDE) works — Jakarta Mail has no exotic runtime requirements.

Add the Jakarta Mail (JavaMail) Library — mind the namespace

The mail API is not included in the JDK. You add it through your build tool, but which artifact you pick determines which import statements compile. This is the single most common thing people get wrong.

<!-- Jakarta Mail 2.x  ->  imports use jakarta.mail.* -->
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>jakarta.mail</artifactId>
    <version>2.0.1</version>
</dependency>

If instead your code imports javax.mail.* (as the classic example below does), you need the 1.6.x artifact, which keeps the old namespace:

<!-- JavaMail 1.6.x  ->  imports use javax.mail.* -->
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>jakarta.mail</artifactId>
    <version>1.6.7</version>
</dependency>

Manual install: If you aren't using Maven or Gradle, download the matching JAR from the Eclipse Jakarta Mail project and add it to your project's build path.

Which version should I use?

JavaMail 1.6.xJakarta Mail 2.x
Import namespaceimport javax.mail.*;import jakarta.mail.*;
Maven version1.6.72.0.1 (or newer)
Min. JavaJava 8Java 8 (2.0.x); Java 11+ for 2.1.x
Framework fitSpring Boot 2.x, Jakarta EE 8, legacy appsSpring Boot 3.x, Jakarta EE 9+, new projects
Which should I use?Only when locked to an existing javax.* codebaseDefault for any new project — it's the maintained line

The rule of thumb: new project → Jakarta Mail 2.x with jakarta.mail.* imports. Only reach for 1.6.x when you're bound to an older javax-based framework. Never mix the two — that mismatch is exactly what produces package javax.mail does not exist even though the dependency is on the classpath.

Advertisement

Complete Java Email Code Example

The example below sends through an SMTP relay that requires TLS and authentication. It's written against the classic javax.mail namespace, so pair it with the 1.6.7 dependency above (or change every javax.mail import to jakarta.mail to use 2.x).

//Import the required libraries for the send email function
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;

//Call the function to send your email.  Pre-populated for Gmail as the SMTP server.
//NOTE: for Gmail, the password below must be a 16-digit App Password, not your account password.
sendEmail("smtp.gmail.com", //SMTP Server Address
"587", //SMTP Port Number
"true", //Enable TLS  (mapped to starttls below)
"true", //Enable Authorization (mapped to auth below)
System.getenv("SMTP_USER"), //Your SMTP Username (from env, not hardcoded)
System.getenv("SMTP_PASS"), //Your SMTP App Password (from env, not hardcoded)
"<From Address>", //Sender Address
"<To Address>", //Recipient Address
"<Subject>", //Message Subject
"<Body>"); //Message Body

The sendEmail Function

//Below is the function for sending the email
static void sendEmail(
        String smtpAddress,
        String smtpPort,
        String enableTLS,
        String enableAuth,
        final String username,
        final String password,
        String fromAddress,
        String toAddress,
        String mySubject,
        String myMessage) {

    Properties props = new Properties();
    props.put("mail.smtp.starttls.enable", enableTLS);
    props.put("mail.smtp.auth", enableAuth);
    props.put("mail.smtp.host", smtpAddress);
    props.put("mail.smtp.port", smtpPort);

    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(fromAddress));
        message.setRecipients(Message.RecipientType.TO,
            InternetAddress.parse(toAddress));
        message.setSubject(mySubject);
        message.setText(myMessage);
        Transport.send(message);
        System.out.println("Sent");

    } catch (MessagingException e) {
        // Log the real cause instead of swallowing it — see the troubleshooting table
        e.printStackTrace();
    }
}

One correction to the classic version of this snippet: the third and fourth arguments are labelled "TLS" and "Auth" but the original code wired the TLS value into mail.smtp.starttls.enable and the Auth value into mail.smtp.auth. Keep the argument names and the property keys aligned, or you'll toggle the wrong switch.

How the Code Works

  • SMTP Properties configure the connection — host, port 587, and the STARTTLS/auth toggles.
  • Session binds those properties to an Authenticator that supplies credentials on demand.
  • MimeMessage builds the actual envelope: from, to, subject, and body.
  • Transport.send() opens the connection, upgrades to TLS, authenticates, and streams the message to the relay.

To send from Java you always connect to an SMTP server — the machine responsible for relaying your message onward. Gmail, Outlook, SendGrid, Amazon SES, and self-hosted Postfix all speak SMTP, and all of them require authentication plus encryption for authenticated client submission.

Security note: Store SMTP credentials in environment variables or a secrets manager, as shown above with System.getenv. Never hardcode passwords — they leak through Git history and decompiled JARs.

Troubleshooting: symptom → cause → fix

SymptomLikely causeFix
package javax.mail does not existNamespace/version mismatch, or dependency missingMatch imports to the artifact: javax.mail.* ↔ 1.6.x, jakarta.mail.* ↔ 2.x
535-5.7.8 Username and Password not accepted (Gmail)Using account password after Sept 2022 cutoffEnable 2-Step Verification, generate a 16-digit App Password, use it as the SMTP password
javax.mail.AuthenticationFailedExceptionWrong credentials, or auth disabled on the relayVerify username/password; set mail.smtp.auth=true
Hangs then couldn't connect to host, port: 587Port blocked by firewall/ISP, or wrong hostTest with telnet host 587; try 465 with mail.smtp.ssl.enable=true
MessagingException: Could not convert socket to TLSRelay requires TLS but STARTTLS not setSet mail.smtp.starttls.enable=true (or use port 465 + implicit SSL)
Prints "Sent" but mail never arrivesMissing SPF/DKIM/DMARC → filtered as spamAdd authentication records to the sending domain; check the recipient's spam folder
IllegalStateException: Not connected on reuseReusing a closed Transport across sendsCall Transport.send() per message, or manage connect()/close() explicitly

Next Steps and Extensions

You now have a reusable function that connects to any SMTP relay with authentication and TLS — a foundation for alerts, onboarding mail, and password-reset links. From here:

  • HTML bodies — swap setText for setContent(html, "text/html; charset=utf-8").
  • Attachments — build a MimeMultipart with a body part per file.
  • CC/BCC — add recipients with Message.RecipientType.CC / .BCC.
  • Provider APIs — for volume or deliverability guarantees, move to SendGrid or Amazon SES over their REST APIs instead of raw SMTP.
  • Deliverability — the code is the easy part; getting mail into the inbox is a DNS problem. See the related guides below on SPF/DKIM/DMARC and delivery troubleshooting.

Frequently Asked Questions

How do I send an email in Java?

Add the Jakarta Mail (JavaMail) library to your project, build a Properties object with your SMTP host, port, STARTTLS and auth flags, create a Session with an Authenticator that supplies your username and password, then construct a MimeMessage and call Transport.send(message). The JDK does not ship an SMTP client, so the external Jakarta Mail dependency is required.

Why does my Java email code say "package javax.mail does not exist"?

You either forgot the Jakarta Mail dependency or you mixed namespaces. Jakarta Mail 2.x uses the jakarta.mail.* package, while 1.6.x still uses javax.mail.. If your imports say javax.mail. you must use the 1.6.x artifact (com.sun.mail:jakarta.mail:1.6.7); if you use 2.x you must change every import to jakarta.mail.*. Mixing the two is the single most common cause of the "package does not exist" error.

Can I still send Gmail with my regular password from Java?

No. Google disabled "less secure app" password sign-in for SMTP in September 2022. You must enable 2-Step Verification and generate a 16-digit App Password, then use that App Password as the SMTP password. For production apps, use OAuth 2.0 (XOAUTH2) instead. Plain account passwords will be rejected with an authentication error.

What SMTP port and encryption should I use?

Use port 587 with STARTTLS for almost all modern relays (Gmail, Outlook, SendGrid, Amazon SES). Port 465 uses implicit TLS (SSL on connect) and is also fine but requires mail.smtp.ssl.enable instead of the starttls flag. Port 25 is for server-to-server relay and is widely blocked by ISPs and cloud providers for authenticated client sending.

How do I send an HTML email instead of plain text in Java?

Replace message.setText(body) with message.setContent(htmlBody, "text/html; charset=utf-8"). For emails that need both an HTML and a plain-text fallback, build a MimeMultipart with two MimeBodyPart objects and set the subtype to "alternative".

How do I add an attachment to a Java email?

Use a MimeMultipart. Add one MimeBodyPart for the message text, then add a second MimeBodyPart and call attachFile("/path/to/file.pdf") on it (or use a DataHandler with a FileDataSource). Finally call message.setContent(multipart) before Transport.send().

Why does my email send successfully but never arrive?

Transport.send() only confirms the SMTP relay accepted the message, not that it reached the inbox. Missing SPF, DKIM, or DMARC records on your sending domain are the usual reason mail lands in spam or is dropped silently. Check the receiving server's spam folder and verify your domain's email authentication records.

Should I hardcode SMTP credentials in my Java source?

Never. Store SMTP username and password in environment variables, a secrets manager, or an externalized config file that is excluded from version control. Hardcoded credentials leak through Git history and compiled JARs, and are a frequent source of account compromise.

Is JavaMail the same as Jakarta Mail?

Yes. When Java EE moved to the Eclipse Foundation, the javax.* namespace was renamed to jakarta.* for trademark reasons. JavaMail became Jakarta Mail. The API is functionally identical; only the package prefix and some Maven coordinates changed between the 1.6.x (javax) and 2.x (jakarta) releases.

Advertisement