Skip to content

Repository files navigation

APACHE v2 License Latest Release Javadocs Codacy

smtp-connection-pool

smtp-connection-pool keeps Jakarta Mail Transport connections open and leases each physical connection exclusively to one caller at a time. It supports lazy or eager allocation, bounded waiting, expiration, and clusters of SMTP servers.

It does not build messages or decide how SMTP works. The selected Jakarta Mail provider still owns authentication, TLS, EHLO, PIPELINING, CHUNKING, response parsing, and the actual send.

Version 4.0.0 expands the project beyond its direct API with optional Jakarta Mail and Camel integrations. Existing users can keep the same org.simplejavamail:smtp-connection-pool dependency and direct API.

Start here: executable demos

The best introduction is the non-published demo project. Every example runs against a real dummy SMTP server on a random port and asserts message delivery, physical connection reuse, and clean shutdown.

Runnable example Integration shown Verified result
DirectPoolDemo Direct leases, release, forced failure, invalidation, and recovery 3 messages over 1 connection; then replacement after a dropped connection
SimpleJavaMailDemo Simple Java Mail as a higher-level library built directly on the pool 3 messages over 1 connection
BatchModuleDemo Standalone Simple Java Mail batch callbacks over caller-created Jakarta Mail messages 3 messages over 1 connection
JakartaMailDemo Plain Jakarta Mail with smtppool 3 messages over 1 connection
SpringDemo Spring JavaMailSenderImpl with smtppool 3 messages over 1 connection
CamelDemo Camel with the separate smtppool: adapter 3 messages over 1 connection

Run the complete executable suite with JDK 21:

mvn -pl smtp-connection-pool-demo -am test

Or run DemoLauncher or any individual demo directly from IntelliJ. The demo is built and tested with the rest of the project, but is deliberately excluded from Maven Central. BatchModuleDemo uses the supported standalone path-2 API published in Simple Java Mail 9.3.0 through #698.

Choose one orchestration owner

Start by deciding whether you need a pool at all. Then choose the one layer that should own it:

Option Best when Orchestration owner Lease handling Pooling/clustering
No pool: withOpenConnection / simple batch One sequential unit of work Application / Simple Java Mail Not applicable No
Simple Java Mail Mailer + batch-module Using EmailBuilder and Mailer Simple Java Mail Automatic Yes
Standalone batch-module facade Creating MimeMessage objects while wanting managed callbacks and futures Batch facade Automatic Yes
Direct smtp-connection-pool Needing exact claim, failure, and shutdown control Application Explicit lease Yes
Jakarta smtppool provider Plain Jakarta Mail or Spring owns Transport calls Provider Mapped to connect / close Yes
Camel smtppool: adapter Camel owns endpoints and component lifecycle Camel adapter / provider Automatic Yes

Exactly one component owns the physical connection pool. Never place batch-module or a direct pool around an smtppool transport.

The complete chooser, ownership model, and examples live in Simple Java Mail's SMTP connection pooling and batch orchestration guide.

The pooled choices reduce to three integration paths, not three abstraction levels inside this repository.

Path Choose it when Who manages the pool Status
1. Use the pool directly Your application or a higher-level library needs clustering, explicit leases, and complete failure/shutdown control. Simple Java Mail itself belongs here. Your application or library Available; explicit SmtpTransportLease is available since 4.0.0
2. Use Simple Java Mail's batch-module directly You create Jakarta Mail messages yourself but want Simple Java Mail's asynchronous batch engine and a safe callback API without adopting EmailBuilder and Mailer. Simple Java Mail's batch API Available since Simple Java Mail 9.3.0 through #698
3. Use it as a Jakarta Mail Transport Plain Jakarta Mail, Spring, or Camel already obtains and closes Transport instances. PooledTransport Available since 4.0.0 through #10

Simple Java Mail stays on path 1 internally. Path 2 is a narrower public API over part of its batch engine. Path 3 presents the pool as a normal Jakarta Mail transport protocol.

The architecture, ownership rules, and five flow diagrams are in PRODUCT-VISION.md.

Artifacts

All three published modules are released together at one version. Maven Central also receives the shared smtp-connection-pool-parent POM required by Maven; it is build metadata, not a fourth application dependency.

Artifact Purpose Java
org.simplejavamail:smtp-connection-pool Direct and clustered pool APIs plus SmtpTransportLease 8+
org.simplejavamail:smtp-connection-pool-jakarta-provider Discoverable smtppool Jakarta Mail provider and Session-scoped lifecycle registry 8+
org.simplejavamail:smtp-connection-pool-camel Optional Camel Mail selection adapter; pooling remains in the provider module 17+ (Camel 4.21)

JPMS module names

Starting with 4.0.1, every published JAR declares a stable Automatic-Module-Name:

Artifact Module name
smtp-connection-pool org.simplejavamail.smtpconnectionpool
smtp-connection-pool-jakarta-provider org.simplejavamail.smtpconnectionpool.jakarta
smtp-connection-pool-camel org.simplejavamail.smtpconnectionpool.camel

The transitive object-pool chain is stable as well: generic-object-pool 2.4.2 declares org.bbottema.genericobjectpool, and clustered-object-pool 4.0.3 declares org.bbottema.clusteredobjectpool. The build inspects every packaged manifest and compiles a real module-path consumer requiring all five names.

The repository also contains smtp-connection-pool-demo. It is an example project tested with every build, not a fourth published module, and is explicitly excluded from Maven Central.

The provider module lets Jakarta Mail, Spring, and Camel obtain pooled Transport instances; it does not speak SMTP itself. Applications still supply Angus Mail or another compatible SMTP Transport provider. Each underlying Transport must represent one reusable physical connection—do not hide a second connection pool beneath this one.

Path 1: use the pool directly

<dependency>
    <groupId>org.simplejavamail</groupId>
    <artifactId>smtp-connection-pool</artifactId>
    <version>4.0.1</version>
</dependency>

Create a Session normally, then claim one exclusive lease. Closing an active lease releases it; invalidate it first when a failure makes the connection uncertain.

SmtpConnectionPool pool = new SmtpConnectionPool(new SmtpClusterConfig<Session>());

try (SmtpTransportLease lease = pool.claimTransport(session)) {
    try {
        Session selectedSession = lease.getSession();
        Transport transport = lease.getTransport();
        transport.sendMessage(message, message.getAllRecipients());
    } catch (MessagingException | RuntimeException failure) {
        lease.invalidate();
        throw failure;
    }
}

// Application shutdown: wait until active leases return and connections close.
pool.shutDown().get();

claimTransport can block and throws InterruptedException. Preserve the thread's interruption policy. The default pool is lazy, has a maximum of four physical connections per Session, and makes an available transport eligible for expiration ten seconds after its last claim.

For partial-recipient failures, an advanced integration may release rather than invalidate only when the delegate is demonstrably still connected. Unknown failures should be treated conservatively.

Clustered pools

SmtpClusterConfig<UUID> config = new SmtpClusterConfig<>();
config.getConfigBuilder()
        .defaultCorePoolSize(0)
        .defaultMaxPoolSize(10)
        .loadBalancingStrategy(new RandomAccessLoadBalancing<>())
        .claimTimeout(new Timeout(30, SECONDS));

SmtpConnectionPoolClustered<UUID> pool = new SmtpConnectionPoolClustered<>(config);

ResourceClusterAndPoolKey<UUID, Session> server =
        new ResourceClusterAndPoolKey<>(clusterId, session);
try (SmtpTransportLease lease = pool.claimTransport(server)) {
    lease.getTransport().sendMessage(message, message.getAllRecipients());
}

Clusters and pools are created on demand. Use registerResourceCluster or registerResourcePool when a cluster or server needs different sizing, expiration, or load-balancing behavior.

OAuth2 tokens

The direct allocator resolves a thread-safe token supplier only when it opens or reconnects a physical transport:

session.getProperties().put(
        SmtpConnectionPool.OAUTH2_TOKEN_PROVIDER_PROPERTY,
        (Supplier<String>) tokenProvider::getAccessToken);

The supplier owns caching and refresh. A fixed token remains available through OAUTH2_TOKEN_PROPERTY for short-lived use.

Path 2: Simple Java Mail batch-module

This path is deliberately delivered in Simple Java Mail, not in this repository. Simple Java Mail #698 added a public callback API in Simple Java Mail 9.3.0 for applications that already create Jakarta Mail messages but want asynchronous execution, clustering, and safe release/invalidate handling without adopting the full EmailBuilder/Mailer API.

<dependency>
    <groupId>org.simplejavamail</groupId>
    <artifactId>batch-module</artifactId>
    <version>9.3.0</version>
</dependency>
try (BatchTransportExecutor<String> batch =
             BatchTransportExecutor.<String>builder().build()) {
    batch.registerSession("outbound", session);
    batch.execute("outbound", (selectedSession, transport) -> {
        MimeMessage message = new MimeMessage(selectedSession);
        // populate recipients, subject, and content
        transport.sendMessage(message, message.getAllRecipients());
        return null;
    });
}

BatchTransportExecutor uses SmtpTransportLease internally and remains separate from smtppool. It releases after a successful callback, invalidates after an escaping failure, and owns only the default executor it creates. BatchModuleDemo runs this exact path against a real dummy SMTP server.

Path 3: use it as a Jakarta Mail Transport

Add the provider plus a physical Jakarta Mail implementation:

<dependency>
    <groupId>org.simplejavamail</groupId>
    <artifactId>smtp-connection-pool-jakarta-provider</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>org.eclipse.angus</groupId>
    <artifactId>angus-mail</artifactId>
    <version>2.0.5</version>
</dependency>

The provider registers only smtppool; it never replaces or pretends to be smtp or smtps.

Properties properties = new Properties();
properties.setProperty(SmtpPoolProperties.DELEGATE_PROTOCOL, "smtp"); // default
Session session = Session.getInstance(properties);

Transport transport = session.getTransport("smtppool");
transport.connect(host, port, username, password); // claims and connects if needed
try {
    transport.sendMessage(message, message.getAllRecipients());
} finally {
    transport.close(); // releases a healthy lease; invalidates an unhealthy one
}

Future<?> shutdown = SmtpPoolRegistry.shutdown(session); // graceful
shutdown.get();

Graceful shutdown stops new claims and waits for active leases. If a bounded wait expires, shutdownNow(session) invalidates active leases and returns the same completion handle, which completes only after physical Transport.close() calls finish. A Session can be reused only after that shutdown completes and SmtpPoolRegistry.restart(session) is called explicitly.

When credentials or OAuth tokens rotate for the same endpoint, a new credential-isolated pool becomes current and the superseded pool drains. Its retained credential material is cleared as soon as its last active lease finishes; inactive credential generations do not accumulate.

Spring uses the same provider by configuring JavaMailSenderImpl with protocol smtppool. Camel uses the separate adapter and smtppool: or smtppools: endpoint schemes:

<dependency>
    <groupId>org.simplejavamail</groupId>
    <artifactId>smtp-connection-pool-camel</artifactId>
    <version>4.0.1</version>
</dependency>
to("smtppool://smtp.example.com:587"
        + "?username=user&password=secret&to=recipient@example.com");

See the provider reference and Camel reference for configuration, programmatic delegate selection, credential rotation, and shutdown semantics.

Build and verification

Build the complete project with JDK 21 and Maven. The original pool and Jakarta provider still run on Java 8; the Camel and demo modules require Java 17.

mvn clean verify

Verification runs all module tests, the real-server demo smoke tests, SpotBugs, Javadocs, and a checksum-pinned japicmp comparison with the preceding published 4.0.0 library. CircleCI also compiles and tests the original pool plus Jakarta provider on an actual JDK 8. Its JDK 21 release job versions and publishes all public modules together while leaving out the demo.

Related custom transports

Simple Java Mail #699 may produce a faster physical transport. If it implements Jakarta Mail's synchronous Transport contract with one reusable physical session per instance, it can be selected beneath these paths just like Angus. If it instead uses Simple Java Mail's CustomMailer, it owns its own lifecycle and must not be stacked on this pool.

Release notes

4.0.1 (11 August 2026)

  • #11: declare stable JPMS automatic module names for all three published JARs, consume the fixed object-pool module chain, inspect the packaged manifests, and compile an end-to-end module-path consumer.

4.0.0 (10 August 2026)

  • #10: add the explicit lease API, optional smtppool Jakarta Mail provider, and separate Camel adapter without changing the existing org.simplejavamail:smtp-connection-pool dependency or direct API. Harden credential rotation and graceful/forced shutdown using generic-object-pool 2.4.1 and clustered-object-pool 4.0.2.

3.1.0 (7 August 2026)

  • #9: resolve a current OAuth2 token whenever a physical SMTP transport is opened or reconnected.

3.0.1 (6 July 2026)

  • #8: update clustered-object-pool to 4.0.1 so clustered SMTP pools can use cluster-specific defaults.

About

Lightweight SMTP connection pool with clustering support, wait/release mechanism, connection lifecycle management, eager/lazy loading pool with load balancing and auto-expiry policy support

Topics

Resources

Stars

14 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages