GuicedEE Mail Client
Annotation-driven SMTP mail client for GuicedEE using the Vert.x Mail Client.
Core Concept
Declare SMTP connections with annotations — everything is discovered at startup via ClassGraph, wired through Guice, and managed by the Vert.x Mail Client with connection pooling.
Required Flow
- Add
com.guicedee:mail-clientdependency. - Define a connection on
package-info.java(preferred) or any class:@MailConnectionOptions( value = "notifications", hostname = "smtp.example.com", port = 587, startTls = MailConnectionOptions.StartTLSMode.REQUIRED, login = MailConnectionOptions.LoginMode.REQUIRED, username = "user@example.com", password = "secret", defaultFrom = "noreply@example.com" ) package com.example.mail; - Inject and send:
public class NotificationService { @Inject @Named("notifications") private MailService mailService; public void sendWelcome(String to) { mailService.sendHtml(to, "Welcome!", "<h1>Welcome aboard!</h1>"); } } - Or use full MailMessage for attachments:
MailMessage message = new MailMessage() .setTo("user@example.com") .setSubject("Report") .setText("See attached.") .setAttachment(List.of( MailAttachment.create() .setContentType("application/pdf") .setName("report.pdf") .setData(Buffer.buffer(pdfBytes)) )); mailService.send(message); - Configure
module-info.java:module my.app { requires com.guicedee.mailclient; opens my.app.mail to com.google.guice, com.guicedee.mailclient; }
Annotations
@MailConnectionOptions
SMTP connection configuration: hostname, port, StartTLS, SSL, login credentials, connection pooling, DKIM, keep-alive, default from address. Targets TYPE and PACKAGE.
Injectable Components
| Binding | Key | Description |
|---|---|---|
| MailService | @Named("connection-name") | High-level mail service with convenience send methods |
| MailClient | @Named("connection-name") | Raw Vert.x mail client per connection |
Environment Variable Overrides
Every annotation attribute can be overridden via system properties or environment variables at runtime, scoped by connection name:
MAIL_{NORMALIZED_NAME}_{PROPERTY}— name-specific overrideMAIL_{PROPERTY}— global fallback
Examples:
MAIL_NOTIFICATIONS_HOSTNAME=smtp.prod.example.comMAIL_NOTIFICATIONS_USERNAME=prod-userMAIL_NOTIFICATIONS_PASSWORD=prod-secretMAIL_PORT=465(global fallback)
Startup Flow
IGuiceContext.instance().inject()
└─ MailClientPreStartup (annotation scanning)
├─ Discovers @MailConnectionOptions (classes and package-info.java)
└─ Wraps with environment variable resolution
└─ MailClientModule (Guice bindings)
├─ Creates shared MailClient per connection (pooled)
├─ Binds MailClient as @Named("connection-name")
└─ Binds MailService as @Named("connection-name") singleton
└─ MailClientPostStartup (initialization logging)
└─ MailClientPreDestroy (shutdown)
└─ Closes all mail clients
MailService Methods
| Method | Description |
|---|---|
| send(MailMessage) | Send a pre-built message (auto-fills from if defaultFrom set) |
| sendText(to, subject, text) | Send plain text using default from |
| sendText(from, to, subject, text) | Send plain text with explicit from |
| sendHtml(to, subject, html) | Send HTML using default from |
| sendHtml(from, to, subject, html) | Send HTML with explicit from |
| sendMultipart(from, to, subject, text, html) | Send multipart (text + HTML) |
| getMailClient() | Access underlying Vert.x MailClient |
Non-Negotiable Constraints
- Module must
requires com.guicedee.mailclient;. - Packages using injection must
openstocom.google.guiceandcom.guicedee.mailclient. @MailConnectionOptionscan be placed onpackage-info.java(preferred) or any class.- SPI implementations must be dual-registered (
module-info.java+META-INF/services/).