| type | Domain | |||||
|---|---|---|---|---|---|---|
| title | ErmesMail Domain Concepts | |||||
| description | Core domain objects in ErmesMail — SMTPConfig, EmailModel, EmailService, SendEmailTask, and the SecurityMode enum. | |||||
| tags |
|
Source: src/main/java/com/softinstigate/ermes/mail/SMTPConfig.java
Holds SMTP server connection details. Construction is via static factory methods (not public constructors) to make the security intent explicit.
| Field | Type | Description |
|---|---|---|
hostname |
String |
SMTP server hostname |
port |
int |
SMTP port (typically 25, 587, or 465) |
username |
String |
SMTP auth username |
password |
String |
SMTP auth password |
ssl |
boolean |
Whether SSL-on-connect is enabled |
sslPort |
int |
SSL port (default 465) |
securityMode |
SecurityMode |
Enum expressing the transport security policy |
connectionTimeout |
int |
Socket connection timeout in ms (default 10,000) |
socketTimeout |
int |
Socket read timeout in ms (default 60,000) |
DEFAULT_SSL_PORT = 465DEFAULT_CONNECTION_TIMEOUT = 10_000msDEFAULT_SOCKET_TIMEOUT = 60_000ms
SMTPConfig.forPlain(host, port, user, pass)— plain SMTP, no encryptionSMTPConfig.forSsl(host, port, user, pass, sslPort)— implicit TLS (SMTPS)SMTPConfig.forStartTlsOptional(host, port, user, pass)— upgrade to TLS if availableSMTPConfig.forStartTlsRequired(host, port, user, pass)— fail if STARTTLS not offered
PLAIN | SSL | STARTTLS_OPTIONAL | STARTTLS_REQUIRED
The CLI flags map to these modes: --sslon → SSL, --starttls → STARTTLS_OPTIONAL, --starttls-required → STARTTLS_REQUIRED.
toString()redacts the username ([REDACTED]when non-empty)toSecureString()reportshasCredentials=true/falseinstead of actual values
Source: src/main/java/com/softinstigate/ermes/mail/EmailModel.java
Represents an email message with sender, subject, HTML body, recipients, and attachments.
| Field | Type | Description |
|---|---|---|
from |
String |
Sender email address |
senderFullName |
String |
Sender display name (optional) |
subject |
String |
Email subject |
message |
String |
HTML body content |
to |
List<Recipient> |
TO recipients (private, accessed via getters) |
cc |
List<Recipient> |
CC recipients |
bcc |
List<Recipient> |
BCC recipients |
attachments |
List<Attachment> |
URL-based attachments |
Recipient — record Recipient(String email, String name). Name is optional (null for address-only recipients). The compact constructor validates that email is non-null.
Attachment — record Attachment(String url, String fileName, String description). Attachments are URL-based (not file-based); the URL is converted to java.net.URI then java.net.URL for Commons Email. Both url and fileName are validated as non-null in the compact constructor.
addTo(email, name)/addCc(...)/addBcc(...)— add single recipientsetMultipleTo(List<String>)/setMultipleCc(...)/setMultipleBcc(...)— bulk add from email-only listsaddAttachment(url, fileName, description)— add URL-based attachmentsetTo(...)/setCc(...)/setBcc(...)/setAttachments(...)— replace entire lists
toString()redacts the message body (message='[REDACTED]')toSecureString()reports metadata only: subject length, recipient counts (to/cc/bcc), attachment count
Source: src/main/java/com/softinstigate/ermes/mail/EmailService.java
The primary API entry point. Manages an ExecutorService thread pool for async email delivery. Implements AutoCloseable for use with try-with-resources.
EmailService(SMTPConfig smtpConfig) // pool size = availableProcessors()
EmailService(SMTPConfig smtpConfig, int threadPoolSize) // explicit size (0 = sync only)Objects.requireNonNull(smtpConfig)— throwsNullPointerExceptionif nullIllegalArgumentExceptionifthreadPoolSize < 0- The thread pool is created lazily on the first
send()call, not in the constructor
send(EmailModel)— async. Submits aSendEmailTaskto the executor, returnsFuture<List<String>>(error list).sendSynch(EmailModel)— sync. CallsSendEmailTask.call()directly on the calling thread, returnsList<String>.shutdown()— graceful shutdown with 10-second timeout.shutdown(long timeout)— graceful shutdown with custom timeout (seconds).close()— equivalent toshutdown()(AutoCloseable).
When threadPoolSize == 0, no executor is created. send() executes synchronously and returns an already-completed Future. This is useful when the caller manages concurrency externally (e.g., virtual threads). shutdown() is a no-op.
EmailService is safe to share across threads. The ExecutorService handles concurrent task submission. Each send() call creates a new SendEmailTask instance.
Source: src/main/java/com/softinstigate/ermes/mail/SendEmailTask.java
Implements Callable<List<String>>. Configures and sends a single email via Apache Commons Email HtmlEmail.
- Sets up the
MailcapCommandMapworkaround forjavax.activationMIME type resolution - Creates
HtmlEmailviaHtmlEmailFactory(injectable for testing) - Configures host, port, auth, SSL, STARTTLS from
SMTPConfig - Sets HTML body, subject, from address from
EmailModel - Processes attachments (URL-based, converted to
EmailAttachment) - Adds TO/CC/BCC recipients
- Calls
email.send()and collects anyEmailExceptioninto the error list
When SMTPConfig.securityMode is STARTTLS_OPTIONAL or STARTTLS_REQUIRED, the task calls:
email.setStartTLSEnabled(true)email.setStartTLSRequired(true)(only for STARTTLS_REQUIRED)
If the system property mail.debug is true, the task enables JavaMail debug output. This is used in integration tests to capture TLS handshake evidence.
Source: src/main/java/com/softinstigate/ermes/mail/HtmlEmailFactory.java
A single-method interface for creating HtmlEmail instances. Exists solely for testability — production code uses DefaultHtmlEmailFactory, tests inject a Mockito mock.
public interface HtmlEmailFactory {
HtmlEmail create();
}This pattern was introduced in v2.0.0 to allow SendEmailTask to be tested without a live SMTP connection.
Source: src/main/java/com/softinstigate/ermes/mail/Main.java
Picocli-based CLI entry point. Implements Callable<Integer> with annotated fields for all CLI flags.
- Validates that
--sslonand--starttlsare mutually exclusive - Creates
SMTPConfigvia the appropriate factory method based on flags - Creates
EmailModelfrom CLI args, sets recipients - Uses
EmailServicewith pool size 1 (effectively synchronous) - Returns exit code 0 on success, 1 on error
--passwordsupports interactive prompting (picocliarity = "0..1", interactive = true)
VersionProvider.java reads Implementation-Version from the JAR manifest (set by Maven) and formats a multi-line version display including Picocli version, JVM info, and OS info.