Fork me on GitHub
Simple Java Mail
Simple API, Complex Emails

Simple Java Mail Features

(also check configuration and security if you're missing a feature)

Creating and sending emails is very simple with Simple Java Mail; you don't need to know about the mailing RFC, MimeMessage or any other low level javax.mail API. No anonymous inner classes needed and no large frameworks needed nor XML.

Entry classes

The primary entry classes are EmailBuilder and MailerBuilder. Other entry classes are EmailConverter and JMail (the latter as alternative to using the validation methods on the Mailer instance). Finally, MailerHelper exposes some utilities in case you don't actually need to connect to a server.

Default features

Simple Java Mail will do some basic validation checks so that your email is always populated with enough data. It also checks for CRLF injection attacks. It even verifies email addresses against RFC-2822 and others using JMail. Simple Java Mail also takes care of all the connection and security properties for you.

Builders all the way down

With all the possible ways to configure Email and Mailer instances, the library had only one option left to streamline API, avoid the Telescoping Constructor anti-pattern and keep things manageable: fluent builders.

  1. With fluent builders, we can tightly control valid combinations, logical decision paths and easily provide alternative methods.
  2. At the same time we can concentrate all mutation logic in the builders and produce mostly immutable objects.
  3. Finally, we're able to centralize our documentation around the builders and refer to it from wherever this library is used.

How does Simple Java Mail compare to other mailing libraries?

Checkout the feature comparison matrix to see how Simple Java Mail fares against other libraries, like Apache Commons Email and Spring Mail.

Migrating from an older version?

Checkout the migration notes to see exactly what changed.


Currently everything we can think of is included or on the issue tracker!

Got a suggestion? Please post it in the issue tracker.


Basic usage

Simply build an Email, populate it with your data, build a Mailer and send the Email instance. The mailer can be created with your own Session instance as well.

A Mailer instance is reusable.

Muliple Mailer instances can form a powerful cluster.

Email email = EmailBuilder.startingBlank()
    .from("Michel Baker", "")
    .to("mom", "")
    .to("dad", "")
    .withSubject("My Bakery is finally open!")
    .withPlainText("Mom, Dad. We did the opening ceremony of our bakery!!!")
	.withHTMLText("<p>Mom, Dad. We did the opening ceremony of <strong>our bakery</strong>!!!</p>")

  .withSMTPServer("server", 25, "username", "password")

About the fluent API with the Builder pattern

The entry classes for the builders are EmailBuilder and MailerBuilder.

For EmailBuilder, the first method initializes the builder in different ways, leaving it with defaults (EmailBuilder.startingBlank()), preconfiguring it (EmailBuilder.copying(), EmailBuilder.replyingTo()), or by setting values otherwise not possible (EmailBuilder.forwarding()).

For MailerBuilder, the first method determines if you get a full builder API or a reduced API because you provided your own custom Session instance.
If you provide your own session, a lot of properties are presumed to be preconfigured, such as SMTP server details.


Configure once, reuse many times

You can preconfigure a Mailer and use it many times. It is thread-safe.

Mailer inhouseMailer = MailerBuilder
    .withSMTPServer("server", 25, "username", "password")

Or as preconfigured Spring bean:
public Mailer inhouseMailer() {
    return MailerBuilder
Or the default one from the Spring support module:


@Autowired Mailer mailer; // configured completely using default properties

Alternative API for almost everything

Simple Java Mail has alternative ways to do things for almost everything...

For example, when building an email, you can add recipients using Recipient objects, RFC822 compliant String addresses (each can be comma delimited and include optional nested names), InternetAddress objects, collections of said addresses, default address names or fixed address names.

// You can add your own Recipient instances for example
currentEmailBuilder.withRecipients(yourRecipient1, yourRecipient2...);
// or add comma / semicolon separated addresses (without names)
String list = ",;";
// or:
currentEmailBuilder.bccWithDefaultName("maintenance group", list);
currentEmailBuilder.bccWithFixedName("maintenance group", list); // same as .bcc()
// what about a group with one deviating name?
String list = ",; Security Group <>";
currentEmailBuilder.toWithDefaultName("stakeholders", list);
// bob and gene are named "stakeholders", "Security Group" get its own name

Through properties:,;

To give you an idea of how flexible the API is for just adding recipients:

// TO
.to(Recipient... recipients)
.to(Collection<Recipient> recipients)
.to(String name, String address)
.to(String oneOrMoreAddresses)
.to(String name, String... oneOrMoreAddressesEach)
.to(String name, Collection<String> oneOrMoreAddressesEach)
.toMultiple(String... oneOrMoreAddressesEach)
.toMultiple(Collection<String> oneOrMoreAddressesEach)
.toWithFixedName(String name, String... oneOrMoreAddressesEach)
.toWithDefaultName(String name, String... oneOrMoreAddressesEach)
.toWithFixedName(String name, Collection<String> oneOrMoreAddressesEach)
.toWithDefaultName(String name, Collection<String> oneOrMoreAddressesEach)
.to(String name, InternetAddress address)
.to(InternetAddress address)
.to(String name, InternetAddress... oneOrMoreAddressesEach)
.toAddresses(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.toMultiple(InternetAddress... oneOrMoreAddressesEach)
.toMultipleAddresses(Collection<InternetAddress> oneOrMoreAddressesEach)
.toAddressesWithFixedName(String name, InternetAddress... oneOrMoreAddressesEach)
.toAddressesWithDefaultName(String name, InternetAddress... oneOrMoreAddressesEach)
.toAddressesWithFixedName(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.toAddressesWithDefaultName(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
// CC
.cc(Recipient... recipients)
.cc(Collection<Recipient> recipients)
.cc(String name, String address)
.cc(String oneOrMoreAddresses)
.cc(String name, String... oneOrMoreAddressesEach)
.cc(String name, Collection<String> oneOrMoreAddressesEach)
.ccMultiple(String... oneOrMoreAddressesEach)
.ccAddresses(Collection<String> oneOrMoreAddressesEach)
.ccWithFixedName(String name, String... oneOrMoreAddressesEach)
.ccWithDefaultName(String name, String... oneOrMoreAddressesEach)
.ccWithFixedName(String name, Collection<String> oneOrMoreAddressesEach)
.ccWithDefaultName(String name, Collection<String> oneOrMoreAddressesEach)
.cc(String name, InternetAddress address)
.cc(InternetAddress address)
.cc(String name, InternetAddress... oneOrMoreAddressesEach)
.ccAddresses(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.ccMultiple(InternetAddress... oneOrMoreAddressesEach)
.ccMultipleAddresses(Collection<InternetAddress> oneOrMoreAddressesEach)
.ccAddressesWithFixedName(String name, InternetAddress... oneOrMoreAddressesEach)
.ccAddressesWithDefaultName(String name, InternetAddress... oneOrMoreAddressesEach)
.ccAddressesWithFixedName(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.ccAddressesWithDefaultName(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
// BCC
.bcc(Recipient... recipients)
.bcc(Collection<Recipient> recipients)
.bcc(String name, String address)
.bcc(String oneOrMoreAddresses)
.bcc(String name, String... oneOrMoreAddressesEach)
.bcc(String name, Collection<String> oneOrMoreAddressesEach)
.bccMultiple(String... oneOrMoreAddressesEach)
.bccAddresses(Collection<String> oneOrMoreAddressesEach)
.bccWithFixedName(String name, String... oneOrMoreAddressesEach)
.bccWithDefaultName(String name, String... oneOrMoreAddressesEach)
.bccWithFixedName(String name, Collection<String> oneOrMoreAddressesEach)
.bccWithDefaultName(String name, Collection<String> oneOrMoreAddressesEach)
.bcc(String name, InternetAddress address)
.bcc(InternetAddress address)
.bcc(String name, InternetAddress... oneOrMoreAddressesEach)
.bccAddresses(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.bccMultiple(InternetAddress... oneOrMoreAddressesEach)
.bccMultipleAddresses(Collection<InternetAddress> oneOrMoreAddressesEach)
.bccAddressesWithFixedName(String name, InternetAddress... oneOrMoreAddressesEach)
.bccAddressesWithDefaultName(String name, InternetAddress... oneOrMoreAddressesEach)
.bccAddressesWithFixedName(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.bccAddressesWithDefaultName(String name, Collection<InternetAddress> oneOrMoreAddressesEach)
.withRecipientsWithDefaultName(String defaultName, Collection<String> oneOrMoreAddressesEach, RecipientType recipientType)
.withRecipientsWithFixedName(String fixedName, Collection<String> oneOrMoreAddressesEach, RecipientType recipientType)
.withRecipientsWithDefaultName(String name, RecipientType recipientType, String... oneOrMoreAddressesEach)
.withRecipientsWithFixedName(String name, RecipientType recipientType, String... oneOrMoreAddressesEach)
.withRecipients(String name, boolean fixedName, RecipientType recipientType, String... oneOrMoreAddressesEach)
.withRecipients(String name, boolean fixedName, Collection<String> oneOrMoreAddressesEach, RecipientType recipientType)
.withAddressesWithDefaultName(String defaultName, Collection<InternetAddress> addresses, RecipientType recipientType)
.withAddressesWithFixedName(String fixedName, Collection<InternetAddress> addresses, RecipientType recipientType)
.withAddresses(String name, boolean fixedName, Collection<InternetAddress> addresses, RecipientType recipientType)
.withRecipients(Collection<Recipient> recipients)
.withRecipients(Recipient... recipients)
.withRecipients(Collection<Recipient> recipients, RecipientType fixedRecipientType)
.withRecipient(String singleAddress, RecipientType recipientType)
.withRecipient(String name, String singleAddress, RecipientType recipientType)
.withRecipient(Recipient recipient)

Authentication and OAUTH2 support

You use one of the builder methods on MailerBuilder for defining the server properties like host, port, username and password.

Simple Java Mail supports plain SMTP (default, but not recommended), SMTPS (legacy SSL) or TLS (recommended). The last authentication option to join the family is OAuth2 (by means of XOAUTH2 which comes built-in in Jakarta Mail, the underlying SMTP framework).

Then depending on the transport strategy that you choose, the password represents the SMTP server password or the OAuth2 token. However, obtaining the OAuth2 token and refreshing tokens etc. is outside the scope of this library, which varies from platform to platform.

  .withSMTPServer("server host", 587, "username", yourPassword)
  .withSMTPServer("server host", 587, "username", yourOAuth2Token)

See the security page for a more in-depth explanation of transport strategies.


Asynchronous parallel batch sending and clustering

The default mode is to send emails synchronously, blocking execution until the email was processed completely and the STMP server sent a successful result.

You can also send anyschronously in parallel or batches, or simply send in a fire-and-forget way. If an authenticated proxy is used, the proxy bridging server is kept alive until the last email has been sent.

Depending on the SMTP server (and proxy server if used) this can greatly influence how fast emails are sent.

mailer.sendMail(email, /* async = */ true);
Or configure it when building the mailer:

Mailer mailer = mailerBuilder


Refer to the configuration section on how to set the thread pool size default or how to configure the executor service (thread pool manager).

Advanced batch processing
You can maximize performance by including the batch-module, which allows you to pool the (Transport) connection over multiple sends, which generally saves 50% time for simpler emails). Going further, you can allow for multiple pooled connections to the same server and further still: even cluster multiple pools of connections to different servers!

Refer to the configuration section for details.


Handling asynchronous mailing result

To handle the successful or erroneous result of an async sent email, you can just register your handlers on the returned CompletableFuture.

Also read this excellent primer on how to process exceptions with CompletableFuture.

Note that non-async sending or testing connections return a CompletableFuture that is completed immediately.

CompletableFuture<Void> f = mailer.sendMail(email, true);
// also mailer.testConnection(email, true)
// or mailerBuilder.async() and then just mailer.sendMail(email)

// one of the many ways you can handle the result:
	.whenComplete((result, ex) -> {
		if (ex != null) {
			System.err.printf("Execution failed %s", ex);
		} else {
			System.err.printf("Execution completed: %s", result);

Sending with your own Session instance

If you prefer to use your own preconfigured Session instance and still benefit from Simple Java Mail, you can!

Email email = ...


Changing the content encoding

By default content is encoded in the target MimeMessage or EML using quoted-printable, so the EML is nicely and safely readable. However, you can change it to many other encoders such as base64, which for example is useful if you want to add an extra layer of obfuscation.

The email header that governs this feature is Content-Transfer-Encoding and Simple Java Mail takes care of inserting this header in the right places, which varies depending on the nescesary email structure.

quoted-printable (the default) results in the following EML subsection:
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

We should meet up!
base64 on the other hand, produces the following:
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64


Encoding attachments

You can control encoding seperately for attachments, so your text files for examples can be encoded differently too.

.withAttachment("invitation.pdf", yourDataSource,
                "Invitation flyer", ContentTransferEncoding.BASE_64)

// or fix encoding by creating attachment objects directly
new AttachmentResource(..., ContentTransferEncoding.BINARY)

Available encoders


	// or default back to quoted-printable

Setting a custom message ID on sent email

Message id's are normally generated by the underlying Jakarta Mail framework, but you can provide your own if required.

Just make sure your own id's conform to the rfc5322 msg-id format standard


Setting a custom sent date on sent email

Message sent date is normally filled with the current date, but you can provide your own date if required.

currentEmailBuilder.fixingSentDate(new GregorianCalendar(2011, APRIL, 1, 3, 51).getTime());

Getting the generated email id after sending

Sometimes you need the actual ID used in the MimeMessage that went out to the SMTP server. Luckily, it's very easy to retrieve it.

mailer.sendMail(email); // id updated during sending!
email.getId(); // <1420232606.6.1509560747190@Cypher>

Sending with SSL and TLS

Activating SSL or TLS is super easy. Just use the appropriate TransportStrategy enum.

Email email = ...;

MailerBuilder.withTransportStrategy(TransportStrategy.SMTP); // default if omitted
Or with property default:

Customizing SSL connections further
For maximum control, you can provide your own SSLSocketFactory too:

	.withCustomSSLFactoryInstance(theInstance) // takes precedence
Or with property default:

SSL and TLS with Google mail

Here's an example of SSL and TLS using gMail.

If you have two-factor login turned on, you need to generate an application specific password from your Google account.

  .withSMTPServer("", 25, "your user", "your password")
  // or
  .withSMTPServer("", 587, "your user", "your password")
  // or
  .withSMTPServer("", 465, "your user", "your password")

Adding attachments

You can add attachments very easily, but you'll have to provide the data yourself. Simple Java Mail accepts byte[] and DataSource objects.

    .withAttachment("dresscode.txt", new ByteArrayDataSource("Black Tie Optional", "text/plain"))
    .withAttachment("location.txt", "On the moon!".getBytes(Charset.defaultCharset()), "text/plain")
    // ofcourse it can be anything: a pdf, doc, image, csv or anything else
    .withAttachment("invitation.pdf", new FileDataSource("invitation_v8.3.pdf"))
	// you can provide your own list of attachments as well

If for some reason you need the Content-Description header set as well, you can provide a content description on any attachment.

	        new ByteArrayDataSource("Black Tie Optional", "text/plain"),
	        "The dresscode for the party")
Which results in something like this:
Content-Type: text/plain; filename="dresscode.txt"; name="dresscode.txt"
Content-Disposition: attachment; filename="dresscode.txt"
Content-ID: <dresscode.txt>
Content-Description: The dresscode for the party

Black Tie Optional

You can further control how the attachment is encoded in the MimeMessage / EML until the data read back from the MimeMessage, by providing a content transfer encoding (resulting in a Content-Transfer-Encoding header for the attachment).


Embedding images

Embedding images dead simple with two options:

  1. manual embedding: add cid: placeholders in the HTML yourself
  2. auto resolution: enable auto-resolving image sources to files, class path resources or URL's

Manual embedding:

currentEmailBuilder.withEmbeddedImage("smiley", new FileDataSource("smiley.jpg"));
currentEmailBuilder.withEmbeddedImage("thumbsup", parseBase64Binary(base64String), "image/png");
// above example is included in the demo package in

// the corresponding HTML should contain the placeholders
<p>Let's go!</p><img src='cid:thumbsup'><br/>
<p>Smile!</p><img src='cid:smiley'>

Auto resolution can be enabled for files, classpath resources and URL's and you can configure base dirs and optionally enforce that referenced resources should all be resolved successfully. Useful for when you allow end users to freely enter HTML.

Auto resolution:

<p>Let's go!</p><img src='smiley.jpg'><br/>
<p>Smile!</p><img src=''>

// results in the following HTML when building the email:
<p>Let's go!</p><img src='cid:etweffxdeu'><br/>
<p>Smile!</p><img src='cid:sienfddiew'>

email.getEmbeddedImages(); // now contains two data sources!

To enable this:

	// enable auto resolution
	.withEmbeddedImageAutoResolutionForFiles(true) // default false
	.withEmbeddedImageAutoResolutionForClassPathResources(true) // default false
	.withEmbeddedImageAutoResolutionForURLs(true) // default false

	// support for base dirs
	.withEmbeddedImageBaseDir(RESOURCES_PATH + "/images")

	// allow resources outside of basedir (careful, potential security attack surface!)
	.allowingEmbeddedImageOutsideBaseDir(true) // default false
	.allowingEmbeddedImageOutsideBaseClassPath(true) // default false
	.allowingEmbeddedImageOutsideBaseUrl(true) // default false

	// fail if a resource couldn't be resolved
	.embeddedImageAutoResolutionMustBeSuccesful(true) // default false (lenient mode)
Also works with properties:

Setting custom headers

Sometimes you need extra headers in your email because your email server, recipient server or your email client needs it. Or perhaps you have a proxy or monitoring setup in between mail servers. Whatever the case, adding headers is easy.

    .withHeader("X-Priority", 2);
    .withHeader("X-MC-GoogleAnalyticsCampaign", "halloween_sale");
    .withHeader("X-MEETUP-RECIP-ID", "71415272");
    .withHeader("X-my-custom-header", "foo");
    // or

Setting custom properties on the internal Session

In case you need to modify the internal Session object itself, because you need a tailored configuration that is supported by the underlying javax.mail, that too is very easy.

    .withProperty("mail.smtp.timeout", 30 * 1000)
    .withProperty("mail.smtp.connectiontimeout", 10 * 1000)
    // or

You can also set some default properties to automatically be added.

Every property prepended with simplejavamail.extraproperties will be loaded directly on the internal Session object.

Sending a Calendar event (iCalendar vEvent)

You want to send a nice Calendar event (.ics) that a client such as Outlook processes nicely?


Produce a Calendar event String (manually or by using a library such as ical4j) and pass it to the EmailBuilder.

See the test demo app included in the Simple Java Mail source for a working example.

// Create a Calendar with something like ical4j
Calendar icsCalendar = new Calendar();
icsCalendar.getProperties().add(new ProdId("-//Events Calendar//iCal4j 1.0//EN"));

(..) // add attendees, organizer, end/start date and whatever else you need

// Produce calendar string
ByteArrayOutputStream bOutStream = new ByteArrayOutputStream();
new CalendarOutputter().output(icsCalendar, bOutStream);
String yourICalEventString = bOutStream.toString("UTF-8")

// Let Simple Java Mail handle the rest
    .withCalendarText(CalendarMethod.REQUEST, yourICalEventString)

Direct access to the internal Session

For emergencies, you can also get a hold of the internal Session instance itself. You should never need this however and if you do it means Simple Java Mail failed to simplify the configuration process for you. Please let us know how we can help alleviate this need.

Mailer mailer = ...;

Session session = mailer.getSession();
// do your thing with session

Configure delivery / read receipt

For servers and clients that support it (mostly Outlook at offices), Simple Java Mail has built in support for 'delivery receipt' and 'read receipt', which is configured through the headers Return-Receipt-To and Disposition-Notification-To respectively.

You can explicitly define the email address to return the receipts to or else Simple Java Mail will default to the replyTo address if available or else the fromAddress.

If you simply enabled Return-Receipt-To or Disposition-Notification-To without providing an address, it will default to the first Reply-To recipient if provided or From recipient otherwise.

    // or:
    .withDispositionNotificationTo(new Recipient("name", ""));
    .withReturnReceiptTo(new Recipient("name", ""));

Validating Email Addresses

Simple Java Mail can validate your email addresses. It's not just a simple regex check, but a complete and robust full validation against RFC-2822 and others. It does this by including JMail in the library.

Address validation is performed automatically when sending emails, but you can also directly perform validations.

See JMail for more examples and configurations.

    		.withRule(email -> email.localPart().startsWith("allowed"))
    // or
    .clearEmailValidator() // turn off validation
    .resetEmailValidator() // reset to default (strict)
// you can also directly perform validations:
mailer.validate(email); // does all checks including address validation

// or just do the address validation

// or, fine-tuned to be stricter

Note: any email address validation behaviour you have defined will be overridden if you disable client-side validations completely (which also disables CRLF injection scanning).

    // or
    .resetDisableAllClientValidations() // reset to default (false)

Converting between, Email, MimeMessage, EML and Outlook .msg

With Simple Java Mail you can easily convert between email types. This includes reading S/MIME protected emails from file.

For example, if you need a MimeMessage, you can convert Email objects, EML data and even Outlook .msg files.

If you already have a MimeMessage, you can convert it into an Email instance, complete with embedded images and attachments (or just the metadata), headers intact.

You can even build a mass Outlook .msg to EML converter if you like!

To enable Outlook message parsing support, include the outlook-module. To enable S/MIME signed content and decryption support, include the smime-module

 * Most conversion methods support an optional Pkcs12Config config for handling S/MIME

// from Email
String eml =              EmailConverter.emailToEML(yourEmail);
MimeMessage mimeMessage = EmailConverter.emailToMimeMessage(yourEmail);
MimeMessage mimeMessage = EmailConverter.emailToMimeMessage(yourEmail, yourSession);

// from MimeMessage
Email email =             EmailConverter.mimeMessageToEmail(yourMimeMessage);
String eml =              EmailConverter.mimeMessageToEML(yourMimeMessage);

// from EML
Email email =             EmailConverter.emlToEmail(emlDataString);
MimeMessage mimeMessage = EmailConverter.emlToMimeMessage(emlDataString);
MimeMessage mimeMessage = EmailConverter.emlToMimeMessage(emlDataString, yourSession);

// from Outlook .msg
Email email =             EmailConverter.outlookMsgToEmail(readToString("yourMessage.msg"));
Email email =             EmailConverter.outlookMsgToEmail(new File("yourMessage.msg"));
Email email =             EmailConverter.outlookMsgToEmail(getInputStream("yourMessage.msg"));
String eml =              EmailConverter.outlookMsgToEML(readToString("yourMessage.msg"));
String eml =              EmailConverter.outlookMsgToEML(new File("yourMessage.msg"));
String eml =              EmailConverter.outlookMsgToEML(getInputStream("yourMessage.msg"));
MimeMessage mimeMessage = EmailConverter.outlookMsgToMimeMessage(readToString("yourMessage.msg"));
MimeMessage mimeMessage = EmailConverter.outlookMsgToMimeMessage(new File("yourMessage.msg"));
MimeMessage mimeMessage = EmailConverter.outlookMsgToMimeMessage(getInputStream("yourMessage.msg"));

Pkcs12Config myKeyInfo = Pkcs12Config.builder()

Email decryptedEmail =    EmailConverter.emlToEmail(emlDataString, myKeyInfo);
Email decryptedEmail =    EmailConverter.mimeMessageToEmail(yourMimeMessage, myKeyInfo);
Email decryptedEmail =    EmailConverter.mimeMessageToEmail(yourMimeMessage, myKeyInfo, /*fetchAttachments*/ false);
emailBuilder   	     =    EmailConverter.mimeMessageToEmailBuilder(yourMimeMessage, /*Pkcs12Config*/ null, /*fetchAttachments*/ true);

Setting custom recipient for bouncing emails

For bouncing emails, you can provide a hint to the SMTP server to which bouncing emails should be returned. This is also known as the Return-Path or Envelope FROM and is set on the Session instance with the property mail.smtp.from.

Simple Java Mail offers a convenience method to set this property.

// in similar fashion to setting replyTo address:
    .withBounceTo(aRecipientInstance) // or
    .withBounceTo("Bob", "")
    // or using one of the many alternative methods...

Replying to and forwarding emails

If you have an email you want to reply to or wish to forward, the EmailBuilder has you covered.

Note: due to the nature of the underlying Jakarta Mail framework (also see reply / forward):

  • In case of replying, the original email is quoted in the body of the reply itself.
  • In case of forwarding, the original email is included as a separate body inside the forward.

Replying to an email:

    .replyingTo(receivedEmail) // Email or MimeMessage
    .prependText("Reply body. Original email included below")

Forwarding an email:

    .forwarding(receivedEmail) // Email or MimeMessage
    .text("Hello? This is Forward. See below email:")

Send using a proxy

Simple Java Mail supports sending emails through a proxy. It is also the only java mailing framework in the world that supports sending emails through authenticated proxies. The reason for this is that the underlying native Jakarta Mail framework supports anonymous SOCKS5 proxies, but not authenticated proxies.

To make this work with authentication, Simple Java Mail uses a trick: it sets up a temporary anonymous proxy server for Javax Mail to connect to and then the bridge relays the connection to the target proxy performing the authentication outside of Javax Mail.

This temporary server is referred to as the Proxy Bridging Server.

// anonymous proxy
currentMailerBuilder.withProxy("", 1080)

// authenticated proxy
currentMailerBuilder.withProxy("", 1080, "proxy username", "proxy password");

Refer to the configuration section on how to set proxy server defaults and the port on which the proxy bridge runs.


Testing a server connection

If you just want to do a connection test using your current configuration, including transport strategy and (authenticated) proxy, Simple Java Mail got you covered.

The connection test can also be done asynchronously and the result can be handled asynchronously as well. Take a look at Handling asynchronous mailing result.

// configure your mailer
Mailer mailer = ...;

// perform connection test
mailer.testConnection(/* async? */); // no error means success

Serializing Email objects

Simple Java Mail supports native Java serialization for Email objects with the following caveats:

  • AttachmentResource.dataSource of type DataSource is transient
  • Email.emailToForward of type MimeMessage is transient
  • Email.dkimPrivateKeyInputStream of type InputStream is transient
  • Email.pkcs12ConfigForSmimeSigning of type Pkcs12Config is transient

Plug your own sending logic

You want to benefit from Simple Java Mail's powerful features, but want to replace the actual server testing and email sending with your own?
Simple Java Mail's got your back. Just Define your own CustomMailer and plug it in.

Send mail using MailGun REST API:

Mailer mailGunMailer = MailerBuilder
      .withCustomMailer(new MailGunMailer())
public class MailGunMailer implements CustomMailer {

	public void testConnection(OperationalConfig operationalConfig, Session session) {
		// call MailGun rest service to test the provided config

	public void sendMessage(OperationalConfig operationalConfig, Session session, Email email, MimeMessage message) {
		// call MailGun rest service to send the email!

Limit the maximum email size

Do you know your server's maximum allowed email size? Then it might be helpful to have Simple Java Mail reject emails that exceed this before trying to send them.

The following throws an EmailTooBig exception as the cause in a parent MailerException instance

Mailer mailer = MailerBuilder
      .withMaximumEmailSize(4) // 4 bytes, that's not much

try {
} catch(Exception e) {
	// cause: EmailTooBigException
	// msg: "Email size of 277 bytes exceeds maximum allowed size of 4 bytes"