CLI tool
bbottema/simple-java-mail avatar
bbottema/simple-java-mail

Simple Java Mail builds a Mailer once and keeps Jakarta Mail within reach

Simple API, Complex Emails (Jakarta Mail smtp wrapper)

1,294 stars275 forksJavaApache-2.0

At a glance

What is it?
Simple Java Mail wraps Jakarta Mail in a builder API for constructing MIME messages and configuring an SMTP transport, with eight optional modules for DKIM, S/MIME, pooling, SOCKS proxies, Outlook conversion, Spring, a CLI and OSGi. The design decision worth knowing is that every layer stays replaceable: you can supply your own Session, add raw properties, or take the MimeMessage and send it yourself.
Who is it for?
Adopt Simple Java Mail when you send enough mail that building MIME by hand has become a maintenance cost, and add modules only for the features you use since each brings its own dependencies. Skip it for a handful of messages, where Jakarta Mail directly is less code.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The Mailer is the object you configure once

The README's opening example builds two things: an email and a mailer, and then sends.

java
Mailer mailer = MailerBuilder.withSMTPServer(
        System.getenv("SMTP_HOST"),
        587,
        System.getenv("SMTP_USER"),
        System.getenv("SMTP_PASSWORD"))
    .withTransportStrategy(TransportStrategy.SMTP_TLS)
    .buildMailer();

Then the instruction that matters: the port and transport strategy must match your SMTP server, build the `Mailer` once, reuse it, and close it during application shutdown.

That is the whole architectural pitch in three clauses. A `Mailer` holds SMTP settings, transport policy, defaults, overrides, validation, signing, proxy configuration and delivery behaviour, and the README's section title is configure once, reuse everywhere.

The reason is call sites. Without a shared configured object, every place that sends a message re-specifies the host, the port, the credentials and the TLS strategy, and each of those is a place to get it wrong. With one, application code describes the email and the transport policy lives in one place.

Before sending, two checks are named. `mailer.testConnection()` verifies the SMTP path and `mailer.validate(email)` verifies the message. Both are diagnostics you would otherwise write yourself, and both are methods on the object you already have.

The email side is the ordinary builder shape, with `EmailBuilder.startingBlank()`, a `from`, a `RecipientBuilder` carrying a name, address and recipient type, a subject and a plain text body.

Dependencies are declared for both Maven and Gradle, with the core artifact `org.simplejavamail:simple-java-mail` and the version 9.3.5 that matches the current release.

Eight modules, and the rule about versions

The optional modules table is the part of the README most likely to change how you plan the dependency tree.

| Requirement | Artifact | | --- | --- | | DKIM domain signing | `dkim-module` | | S/MIME signing, encryption, and reading | `smime-module` | | Pooled and clustered delivery | `batch-module` | | Authenticated SOCKS proxies | `authenticated-socks-module` | | Outlook `.msg` parsing and conversion | `outlook-module` | | Spring-managed configuration | `spring-module` | | Command-line sending and validation | `cli-module` | | OSGi and Apache Karaf | `karaf-module` |

The stated rule is: use the same Simple Java Mail version for every module.

That is the kind of instruction that exists because of a specific failure. Each module depends on the core, and with independent version ranges Maven can resolve two different cores into one application, which produces a `NoSuchMethodError` at runtime rather than a compile error.

The second instruction is equally practical. Optional modules extend the same API and add their dependencies only when selected, so the cost of a feature is opt-in rather than inherited. The README also notes that some integration artifacts already depend on `simple-java-mail`, so in those cases you do not need to declare it separately.

The modules guide is said to list each coordinate with its main transitive dependencies and the capabilities it enables, which is the document to read before picking one, because the DKIM module and the batch module pull very different things into a build.

What is deliberately absent is worth noting too. There is no module for attachments or HTML bodies, because those are core, and no module for TLS, because that is core as well. The module boundary is drawn around the things that are genuinely optional infrastructure.

Jakarta Mail stays reachable at every layer

A wrapper library is only safe if it can be got out from under. Simple Java Mail is explicit about that, and the README has a section titled Jakarta Mail remains available.

The relationship is stated in the opening. Jakarta Mail provides the standard mail API, Angus Mail provides its transport implementation, and Simple Java Mail builds well-formed MIME through a streamlined API.

Then four escape hatches are listed. You can build a mailer around your own `Session`. You can add raw Jakarta Mail properties. You can access the generated `MimeMessage`. And you can provide the final send operation yourself.

That last one is the strongest of the four. If Simple Java Mail composes the message correctly and you want a completely different delivery mechanism, an existing queue, an internal relay or a test double, you can take the composed `Email` and send it with your own code.

This matters because the alternative design, a library that never lets you reach the underlying type, is a library you cannot use when Jakarta Mail grows a feature you need. The generated `MimeMessage` is the interchange format between the two layers, and it is the escape hatch for anything the API has not modelled.

The conversion capability listed in the API table follows the same logic in the other direction: conversion between `Email`, `MimeMessage` and EML, send-ready serialization, Outlook `.msg` conversion and custom sending logic. So the three representations are first-class and convertible, not incidental.

That also means the migration cost of leaving is low, which is worth knowing before you adopt any mail library.

Configuration has four sources, and Spring is one of them

The Mailer can be built in four ways, and the README groups them.

The programmatic API is the builder, which is what the opening example uses. Then there are property files, system properties and environment variables, which are the same configuration mechanism in three forms. And the Spring module can create the Mailer and inject it.

The environment variable route is worth dwelling on, because it is what makes the example work in a container. The opening code reads `System.getenv("SMTP_HOST")`, `System.getenv("SMTP_USER")` and `System.getenv("SMTP_PASSWORD")` directly, which passes the credentials through environment variables at runtime.

The Spring module is the other end of the same problem. Rather than reading environment variables at a call site, you let the module build the Mailer once and inject it wherever it is needed, so the wiring is declared rather than constructed.

So the same object is reachable from four different places depending on whether you are writing a standalone application, a Spring application, or something running in a container with secrets injected as environment variables.

The shared-message-rules capability is the other half of this. The API table lists recipient builders, address validation, defaults, enforced overrides, bounce addresses, receipts and maximum message size under shared message rules, which are the policies you would otherwise set per message.

Enforced overrides are the interesting entry there, since they are the opposite of defaults: they cannot be overridden by the caller, which is how you make a rule a rule.

The capability table and what the repository looks like

The README summarises the API surface in six rows, and it is the fastest way to see the scope.

Messages and MIME covers plain text and HTML alternatives, embedded images, attachments, calendar content, headers, encodings and the corresponding multipart structure. That last phrase is the one that matters, because wrong multipart structure is the most common reason a generated email renders badly in some clients and fine in others.

Security covers TLS and SMTPS, server identity and certificate checks, fixed or refresh-aware OAuth2 tokens, header-injection protection, DKIM and S/MIME. Header-injection protection deserves a note: sending mail with unsanitised headers is how you turn a mail library into a spam relay, and having it handled in the library rather than in your call sites is the right place for it.

Delivery covers synchronous and asynchronous sends, a scoped open connection, simple batches, connection pools and independently configured SMTP clusters. The refresh-aware OAuth2 token is what makes this work with providers that expire tokens rather than accepting a password.

Diagnostics covers connection tests, message validation, configuration inspection, Jakarta Mail debug routing, logging integrations and SMTP submission replies.

The repository root is itself informative, and unusually so. There is `modules/` for the module sources, `simplejavamail.org/` for the documentation site, a `pom.xml`, `DEVELOPMENT.md` and `spotbugs-exclude.xml` for static analysis exclusions.

Then there are the process artefacts: `RELEASE.txt`, `RELEASE_HISTORY.md`, `how to release.txt`, `MAINTAINER_WORKFLOW.md`, `API_EXPANSION_WORKFLOW.md` and several planning documents such as `PROJECT_MECHANISMS_CATALOGUE.md` and `MIME_RESOURCE_NAMING_REPORT.md`. A project that keeps its mechanism catalogue and its API expansion workflow in the repository is one where the maintainer expects long-term contributors rather than drive-by patches.

The licence is Apache-2.0, Java 8 is the source, target and minimum supported runtime, and the current release is 9.3.5 from 2026-09-18 with 9.3.4 and 9.3.3 in the preceding fortnight.

Editorial conclusion

Adopt Simple Java Mail when you send enough mail that building MIME by hand has become a maintenance cost, and add modules only for the features you use since each brings its own dependencies. Skip it for a handful of messages, where Jakarta Mail directly is less code. Verify first which version every module is on, because the README requires the same Simple Java Mail version across core and modules, and read the 9.2 migration guide before upgrading from an earlier line.

Frequently asked questions

How do I add Simple Java Mail to a Java project?

With Maven coordinates `org.simplejavamail:simple-java-mail:9.3.5`, or the Gradle equivalent `implementation 'org.simplejavamail:simple-java-mail:9.3.5'`. Java 8 is the minimum supported runtime, and every optional module must use the same version as the core.

Which optional modules does Simple Java Mail provide?

Eight: `dkim-module` for domain signing, `smime-module` for signing, encryption and reading, `batch-module` for pooled and clustered delivery, `authenticated-socks-module` for SOCKS proxies, `outlook-module` for `.msg` parsing, `spring-module` for Spring-managed configuration, `cli-module` for command-line sending and validation, and `karaf-module` for OSGi and Apache Karaf.

Can I still use Jakarta Mail directly with Simple Java Mail?

Yes. You can build a mailer around your own `Session`, add raw Jakarta Mail properties, access the generated `MimeMessage`, or provide the final send operation yourself. Conversion between `Email`, `MimeMessage` and EML is also supported.

Official sources

  1. bbottema/simple-java-mail on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bbottema-simple-java-mail.svg)](https://hysenlabs.com/projects/bbottema-simple-java-mail)

Was this useful?