Developer Guide
This section covers the data model, configuration reference, sending mechanics, and extension points available to developers integrating Email Manager into an application.
Data Model
Email Manager persists two core entities. An email holds the message metadata, recipient lists, and body. An attachment holds the binary content of a file associated with an email. Recipient lists (To, Cc, Bcc) are stored in dedicated collection tables to support multiple addresses per email.
---
config:
layout: elk
---
erDiagram
em_email {
integer email_id PK
varchar senderAddress
varchar subject
text body
boolean html
varchar status
timestamp createdInstant
timestamp sentInstant
text stackTrace
}
em_attachment {
integer attachment_id PK
varchar fileName
varchar fileExtension
timestamp addedInstant
blob data
}
em_recipientAddresses {
integer email_id FK
varchar recipientAddress
}
em_carbonCopyRecipients {
integer email_id FK
varchar carbonCopyRecipients
}
em_blindCarbonCopyRecipients {
integer email_id FK
varchar blindCarbonCopyRecipients
}
em_email ||--o{ em_attachment : "has"
em_email ||--o{ em_recipientAddresses : "sent to"
em_email ||--o{ em_carbonCopyRecipients : "cc'd to"
em_email ||--o{ em_blindCarbonCopyRecipients : "bcc'd to"
The html flag records whether the body must be delivered as HTML or as plain text. The stackTrace column holds the error trace of the last failed delivery attempt, and is cleared when a later attempt succeeds.
Database Schema
Email Manager manages six tables. The schema below represents the DDL generated by Hibernate for a standard relational database. Column names reflect the exact values declared in the entity annotations.
CREATE SEQUENCE em_email_seq
START WITH 1 INCREMENT BY 50;
CREATE SEQUENCE em_attachment_seq
START WITH 1 INCREMENT BY 50;
CREATE TABLE em_email (
email_id INTEGER NOT NULL,
senderAddress VARCHAR(255) NOT NULL,
subject VARCHAR(255),
body TEXT,
html BOOLEAN NOT NULL,
status VARCHAR(255),
createdInstant TIMESTAMP(6) NOT NULL,
sentInstant TIMESTAMP(6),
stackTrace TEXT,
CONSTRAINT pk_em_email PRIMARY KEY (email_id)
);
CREATE TABLE em_attachment (
attachment_id INTEGER NOT NULL,
fileName VARCHAR(255),
fileExtension VARCHAR(255),
addedInstant TIMESTAMP(6),
data BLOB,
CONSTRAINT pk_em_attachment PRIMARY KEY (attachment_id)
);
CREATE TABLE em_email_attachments (
EmailEntity_email_id INTEGER NOT NULL,
attachments_attachment_id INTEGER NOT NULL,
CONSTRAINT pk_em_email_attachments
PRIMARY KEY (attachments_attachment_id),
CONSTRAINT fk_em_email_attachments_email
FOREIGN KEY (EmailEntity_email_id) REFERENCES em_email (email_id),
CONSTRAINT fk_em_email_attachments_attachment
FOREIGN KEY (attachments_attachment_id) REFERENCES em_attachment (attachment_id)
);
CREATE TABLE em_recipientAddresses (
email_id INTEGER NOT NULL,
recipientAddress VARCHAR(255),
CONSTRAINT fk_em_recipientaddresses_email
FOREIGN KEY (email_id) REFERENCES em_email (email_id)
);
CREATE TABLE em_carbonCopyRecipients (
email_id INTEGER NOT NULL,
carbonCopyRecipients VARCHAR(255),
CONSTRAINT fk_em_carboncopyrecipients_email
FOREIGN KEY (email_id) REFERENCES em_email (email_id)
);
CREATE TABLE em_blindCarbonCopyRecipients (
email_id INTEGER NOT NULL,
blindCarbonCopyRecipients VARCHAR(255),
CONSTRAINT fk_em_blindcarboncopyrecipients_email
FOREIGN KEY (email_id) REFERENCES em_email (email_id)
);
The status column stores the string representation of the EmailStatus enum. The body and stackTrace columns are declared as TEXT so that neither the message body nor a full error trace is truncated; the exact type depends on the dialect (CLOB on H2 and Oracle, TEXT on PostgreSQL and MySQL). The data column in em_attachment stores raw binary content; its exact SQL type likewise depends on the dialect (BLOB on MySQL and H2, BYTEA on PostgreSQL). The email-to-attachment relationship uses the Hibernate-generated em_email_attachments join table, as the @OneToMany association carries no explicit @JoinColumn.
Module Overview
Email Manager is structured as six Maven modules following the AppJars layered architecture:
| Module | Artifact ID | Description |
|---|---|---|
| Model | appjars-email-manager-model |
DTOs, EmailStatus enum, filter and sort classes, auto-configuration |
| Business API | appjars-email-manager-business |
EmailService interface |
| Business Impl | appjars-email-manager-business-impl |
Service implementation, MailSenderService, DefaultMailSenderImpl |
| Data API | appjars-email-manager-data |
EmailDao and AttachmentDao interfaces |
| Data Impl | appjars-email-manager-data-impl |
JPA entities and DAO implementations |
| Flow UI | appjars-email-manager-flow |
Vaadin view and route configuration |
A monolithic application includes the three implementation modules (-business-impl, -data-impl, -flow). The API modules (-business, -data) are pulled in transitively.
Spring Auto-Configuration
Email Manager registers itself through Spring Boot's auto-configuration mechanism. The entry point is EmailManagerAutoConfiguration, declared in:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
This class scans the com.appjars.emailmanager package for all Spring components and JPA entities. The business implementation module pulls in spring-boot-starter-mail, which provides the JavaMailSender infrastructure. DefaultMailSenderImpl is registered automatically and configured via the spring.mail.* properties described below.
Configuration Properties
Mail Server
| Property | Default | Description |
|---|---|---|
spring.mail.host |
localhost |
SMTP server hostname |
spring.mail.port |
25 |
SMTP server port |
spring.mail.protocol |
smtp |
Mail transport protocol |
spring.mail.username |
— | SMTP authentication username |
spring.mail.password |
— | SMTP authentication password |
spring.mail.properties.mail.smtp.auth |
true |
Enable SMTP authentication |
spring.mail.properties.mail.smtp.starttls.enable |
true |
Enable STARTTLS encryption |
Email Manager
| Property | Default | Description |
|---|---|---|
com.appjars.emailmanager.url.views.emailcrudview |
em/list |
URL path of the email management view |
com.appjars.emailmanager.from |
— | Comma-separated list of default sender addresses offered in the UI |
email.task.cronexpression |
0/30 * * * * * |
Cron expression controlling how often MailSenderService is triggered when scheduled via Process Manager |
spring.servlet.multipart.max-file-size |
1MB |
Maximum size of individual attachment uploads. Set to 100MB to allow large files |
spring.servlet.multipart.max-request-size |
10MB |
Maximum size of multipart requests. Should match max-file-size |
Email Status Lifecycle
An email moves through a defined sequence of statuses from creation to delivery:
stateDiagram-v2
[*] --> CREATED : email saved
CREATED --> SEND_PENDING : added to queue
SEND_PENDING --> SEND_SUCCESSFUL : sent successfully
SEND_PENDING --> SEND_FAILED : sending failed
CREATED --> SEND_SUCCESSFUL : sent immediately
CREATED --> SEND_FAILED : immediate send failed
| Status | Description |
|---|---|
CREATED |
The email has been saved but not yet submitted for sending |
SEND_PENDING |
The email is queued and will be picked up by the background sender |
SEND_SUCCESSFUL |
The email was accepted by the SMTP server |
SEND_FAILED |
The sending attempt was rejected or produced an error |
Emails can be sent immediately via EmailService.attemptSend(), which transitions directly from CREATED to SEND_SUCCESSFUL or SEND_FAILED. Alternatively, an email can be queued by setting its status to SEND_PENDING, allowing the background MailSenderService to process it in a later batch.
When a delivery attempt fails, the resulting stack trace is stored on the email and can be inspected from the management view. A subsequent successful attempt clears it.
Background Sending
MailSenderService is a Spring-managed Runnable that processes emails in SEND_PENDING status. It pages through them fifty at a time, calling EmailService.attemptSend() for each one, and stops once a page returns fewer than fifty results. The total number of emails processed and successfully sent is logged at the end of the run.
Under a free licence the run aborts as soon as the daily send limit is reached, leaving the remaining emails queued for the next day.
The most straightforward way to run MailSenderService on a schedule is to register it as a Process Manager task. Because it implements Runnable and is a Spring component, it appears automatically in the Processes view if Process Manager is also integrated:
// No additional code required — MailSenderService is a @Component Runnable
// and is discovered by Process Manager's task scanning automatically.
Alternatively, it can be invoked directly via a standard Spring @Scheduled method:
@Scheduled(cron = "${email.task.cronexpression:0/30 * * * * *}")
public void sendPendingEmails() {
mailSenderService.run();
}
Service API
EmailService is the primary interface for managing emails. It extends CrudService and ValidationSupport and exposes the following methods specific to the module:
// Attempt to send an email immediately via SMTP
// Returns true if sent successfully, false if sending failed
boolean attemptSend(EmailDto email);
// Retrieve a paginated, filtered, and sorted stream of emails
Stream<EmailDto> getEmails(int offset, int limit, EmailFilter filter, List<EmailSort> sortOrder);
// Count emails matching a filter
Integer countEmails(int offset, int limit, EmailFilter filter);
// Return the most recently used recipient addresses, up to the requested amount
Set<String> getRecentlyUsedAddresses(int amount);
// Return the number of emails sent during the current day
int getTodayEmailCount();
attemptSend() is transactional: it updates the email status in the database regardless of whether the SMTP call succeeds or fails, ensuring the outcome is always persisted. On failure it also stores the stack trace on the email; on success it clears it.
The service implementation is registered under the bean name AJEmailServiceImpl, and the DAO implementation under AJEmailDaoImpl. Inject them with the matching @Qualifier when the application context contains more than one candidate:
public MyService(@Qualifier("AJEmailServiceImpl") EmailService emailService) {
this.emailService = emailService;
}
Validation is applied on save and update. Both creation and update validators check that the sender address is a valid email format and that at least one recipient address is present and valid. Invalid addresses cause a validation error before any persistence or sending occurs.
Free Licence Restrictions
Without a valid licence, Email Manager runs in free mode with a limit of five emails sent per day. The counter covers successful deliveries only and resets at midnight in the server's default time zone.
Once the limit is reached:
EmailService.attemptSend()throwsFreeLimitReachedExceptioninstead of contacting the SMTP server, and leaves the email inSEND_PENDINGstatus so it can be sent once the counter resets.MailSenderServicestops its current run at the firstFreeLimitReachedException.- In the management view, the Send now and Edit actions are disabled, and queued emails display a Limit reached badge in place of their status.
A restrictions bar above the grid shows the number of emails sent today against the limit whenever the module is running unlicensed. EmailService.getTodayEmailCount() exposes the same counter programmatically.
Customisation
Replacing the Mail Sender
DefaultMailSenderImpl is registered automatically and reads its configuration from the spring.mail.* properties. To use a custom JavaMailSender implementation — for example, one backed by a third-party mail API — define a bean that overrides it:
@Bean
@Primary
public JavaMailSender customMailSender() {
// return a custom JavaMailSender implementation
}
Assigning a Router Layout
By default, Email Manager registers its view without a parent layout. To wrap it in the application's main layout, inject the RouteConfigurer bean using its qualifier and call setViewsRouterLayout in a @PostConstruct method:
@Autowired
@Qualifier("EmailManagerRouteConfigurer")
private RouteConfigurer routeConfigurer;
@PostConstruct
public void configure() {
routeConfigurer.setViewsRouterLayout(MainLayout.class);
}
The @Qualifier is required because multiple AppJars may contribute a bean named RouteConfigurer.
Customising the View URL
The default URL path can be overridden in application.properties:
com.appjars.emailmanager.url.views.emailcrudview=myapp/emails