ErmesMail is a set of Java classes for sending e-mail messages asynchronously, via SMTP servers.
- It can be embedded in your Java project as a tiny wrapper for the Apache Commons Email library.
- It can be used as a handy command line utility, to send emails programmatically from the shell.
ErmesMail is developed in Java 17 and built with Maven 3.8.
JavaDocs are available at the ErmesMail JavaDocs.
- Build the application with maven:
mvn package. - Run the Java executable by passing the following parameters:
$ java -jar target/ermes-mail.jar --help
Usage: java -jar ermes-mail.jar [-v] [--help] [--sslon] [--starttls] [--starttls-required] [-P[=<password>]]
-b=<message> -f=<fromAddress> [-h=<smtpHost>]
[-n=<senderName>] [-p=<smtpPort>] -s=<subject>
[--sslport=<sslPort>] [-u=<user>]
[--bcc=<bccList>[,<bccList>...]...]...
[--cc=<ccList>[,<ccList>...]...]...
--to=<toList>[,<toList>...]... [--to=<toList>[,
<toList>...]...]...
Sends an HTML email to the given recipient(s).
-h, --host=<smtpHost> SMTP host.
-p, --port=<smtpPort> SMTP port.
-u, --user=<user> SMTP user name.
-P, --password[=<password>]
SMTP user password.
--sslon Use SSL.
--sslport=<sslPort> SSL port (default is 465).
--starttls Enable STARTTLS (upgrade to TLS if server supports it).
--starttls-required Require STARTTLS (fail if not supported).
-f, --from=<fromAddress> FROM field.
-n, --sender=<senderName> Sender full name (optional).
-s, --subject=<subject> Subject.
-b, --body=<message> Message body (can be HTML).
--to=<toList>[,<toList>...]...
List of mandatory TO recipients.
--cc=<ccList>[,<ccList>...]...
List of optional CC recipients.
--bcc=<bccList>[,<bccList>...]...
List of optional BCC recipients.
--help display this help message.
-v, --version print version information and exit.
Copyright(c) 2022 SoftInstigate srl (https://www.softinstigate.com)To test the sending of e-mails via command line, we suggest running a local SMTP mock server like Mailpit. Please look at the Mailpit installation instructions for setup details.
After executing Mailpit (usually with the mailpit command) you can send your first HTML email message to localhost with ErmesMail:
$ java -jar target/ermes-mail.jar -h localhost -p 1025 \
-f [email protected] -s "test" -b "This is a <strong>HTML</strong> test email." \
--to [email protected]
mag 24, 2022 4:46:16 PM com.softinstigate.ermes.mail.EmailService <init>
INFORMAZIONI: MailService initialized with SMTPConfig{hostname='localhost', port=1025, username='', ssl=false, sslPort=465}
mag 24, 2022 4:46:16 PM com.softinstigate.ermes.mail.EmailService send
INFORMAZIONI: Sending emails asynchronously...
mag 24, 2022 4:46:16 PM com.softinstigate.ermes.mail.SendEmailTask call
INFORMAZIONI: Processing MailModel{from='[email protected]', senderFullName='null', subject='test', message='This is a <strong>HTML</strong> test email.', to=[Recipient{email='[email protected]', name='null'}], cc=[], bcc=[], attachments=[]}
mag 24, 2022 4:46:16 PM com.softinstigate.ermes.mail.SendEmailTask call
INFORMAZIONI: Email successfully sent!
TO: [Recipient{email='[email protected]', name='null'}]
CC: []
BCC: []
mag 24, 2022 4:46:16 PM com.softinstigate.ermes.mail.EmailService shutdown
INFORMAZIONI: ExecutorService terminated normally after shutdown request.```You can read the e-mail message on the Mailpit UI.
Note: To send messages via Google SMTP, it is necessary to configure your Gmail account by enabling IMAP. More information
To use ErmesMail in your Maven build, first add the JitPack repository in your pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>Then add the following dependency:
<dependency>
<groupId>com.softinstigate</groupId>
<artifactId>ermes-mail</artifactId>
<version>2.1.0</version>
<classifier>shaded</classifier>
</dependency>Warning: As ErmesMail depends on
org.apache.commons.commons-emailv1.6, we suggest including the below runtime dependencies (javax.mail-apiandjavax.mail) to prevent classpath conflicts. The wrong version of these dependencies, included by other libraries, might provoke the following runtime exception when sending emails:java.lang.NoSuchMethodError: 'void com.sun.mail.util.LineOutputStream.<init>(java.io.OutputStream, boolean)
<dependency>
<groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId>
<version>1.6.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
<scope>runtime</scope>
</dependency>You can run mvn dependency:tree in your project to check if other artifacts are including these.
There are two methods for sending emails: EmailService.send is asynchronous and returns a Future list of error strings. The EmailService.sendSynch is synchronous and returns a list of error strings. If the list is empty, it means no errors. However, the SendEmailTask logs exceptions anyway.
You may want to use the asynchronous invocation only in case you have to send many emails in parallel and don't want to block the rest of the program; otherwise, the synchronous method works just fine.
Internally the EmailService uses a java.util.concurrent.ExecutorService to send emails in parallel.
A good understanding of Java Futures would help you implement the best waiting strategy.
Below a java fragment, for example:
// Use the factory methods on SMTPConfig to express the desired security mode.
SMTPConfig smtpConfig = SMTPConfig.forPlain("localhost", 1025, "user", "password");
EmailModel emailModel = new EmailModel(
"[email protected]", "Dick Silly",
"Test email - " + System.currentTimeMillis(),
"This is a <strong>HTML</strong> message.");
emailModel.addTo("[email protected]", "John Doe");
emailModel.addTo("[email protected]", "Serena Wiliams");
emailModel.addCc("[email protected]", "Tom Clancy");
emailModel.addBcc("[email protected]", "Ann Smith");
EmailService emailService = new EmailService(smtpConfig, 3); // 3 threads pool
Future<List<String>> errors = emailService.send(emailModel); // send is async
emailService.shutdown();
List<String> listOfErrors = errors.get(); // WARNING: Future.get() is blocking
if (!listOfErrors.isEmpty()) {
System.err.println("Errors sending emails: " + listOfErrors.toString());
}ErmesMail supports plain SMTP, SSL (SMTPS), and STARTTLS (opportunistic or required).
You can configure the SMTP host, port, user, password, and security mode via the command line or programmatically using the SMTPConfig factory methods.
To test different SMTP configurations:
- Use a local SMTP server like Mailpit for development and testing.
- For SSL (implicit TLS), use the
--sslonand--sslportoptions (default SSL port is 465). - For STARTTLS, use
--starttlsto enable opportunistic STARTTLS, and add--starttls-requiredif you want the client to fail when the server does not advertise STARTTLS. - For plain SMTP, omit the
--sslonand--starttlsflags and use the standard port (usually 25 or 1025 for local testing). - To test with real SMTP providers (e.g., Gmail), ensure your credentials and security settings are correct and that your network/firewall allows outbound connections to the SMTP server and port.
If you encounter issues, check the logs for detailed error messages. For troubleshooting tips, see the project issues or contact the maintainers.
Integration tests are consolidated in IntegrationScenariosIT and cover two scenarios:
local-plain-mailpit: sends plain SMTP to a local Mailpit instance (localhost:1025). This test is executed only when Mailpit is reachable onlocalhost:1025(the test probes the TCP port and will be skipped automatically if nothing is listening).external-smtps-conditional: performs an implicit SSL (SMTPS) send against an external SMTP provider and is run only when integration credentials/configuration are provided via environment variables or a local properties file.
Provide external SMTP configuration either using environment variables or a smtp-integration.properties file in the project root with these keys:
SMTP_INTEGRATION_HOST(e.g.smtps.example.com)SMTP_INTEGRATION_PORT(e.g.465)SMTP_INTEGRATION_USERNAMESMTP_INTEGRATION_PASSWORDSMTP_INTEGRATION_SENDERSMTP_INTEGRATION_RECIPIENTSMTP_INTEGRATION_SSLPORT(optional, defaults to the port above)
Example smtp-integration.properties (do not commit this file):
SMTP_INTEGRATION_HOST=smtps.example.com
SMTP_INTEGRATION_PORT=465
SMTP_INTEGRATION_USERNAME[email protected]
SMTP_INTEGRATION_PASSWORD=supersecret
SMTP_INTEGRATION_SENDER[email protected]
SMTP_INTEGRATION_RECIPIENT[email protected]
SMTP_INTEGRATION_SSLPORT=465Run the tests (integration tests are executed by the Maven verify phase). Use JavaMail debug to capture client-side TLS/SSL handshake logs when the external scenario runs:
mvn -Dmail.debug=true -DfailIfNoTests=false verifyTo run only integration tests while skipping unit tests:
mvn -Dmail.debug=true -DskipTests=true -DfailIfNoTests=false verifyNotes:
- The local Mailpit scenario will be automatically skipped if nothing is listening on
localhost:1025. - The external SMTPS scenario is conditional and will be skipped when the required configuration is not present (either env vars or
smtp-integration.properties/.env). - Older individual integration test files have been removed;
IntegrationScenariosITis the canonical integration test. - Keep integration credentials out of the repository;
smtp-integration.propertiesis ignored by.gitignoreand an example file is provided.
Version 2.0 introduces a breaking change: the boolean-heavy SMTPConfig constructors were removed in favor of explicit factory methods that make the security policy clear.
Old code (pre-2.0):
// Older constructor with boolean flags (removed in 2.0)
SMTPConfig smtpConfig = new SMTPConfig("localhost", 1025, "user", "password", false /*ssl*/);New code (2.0+):
// Use factory methods to express intent clearly
SMTPConfig smtpPlain = SMTPConfig.forPlain("localhost", 1025, "user", "password");
SMTPConfig smtpSsl = SMTPConfig.forSsl("smtp.example.com", 465, "user", "password", 465);
SMTPConfig smtpStartTls = SMTPConfig.forStartTlsOptional("smtp.example.com", 587, "user", "password");
SMTPConfig smtpStartTlsRequired = SMTPConfig.forStartTlsRequired("smtp.example.com", 587, "user", "password");This makes it explicit whether you want plain SMTP, implicit SSL (SMTPS), or STARTTLS (optional or required).