feat(ext-data): close the connection pool when the app stops

TxManager is AutoCloseable and releases what it was built on: JdbcTxManager its data
source when closeable, HibernateTxManager its session factory and then the data source
Hibernate was handed, which Hibernate itself never closes. DataExtension registers the
close as an onClose callback, so a stopped app no longer leaves its pool connected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Zakaria El Orche
2026-09-22 14:02:35 +00:00
co-authored by Claude Opus 5
parent 6d44f9e7b1
commit d6c018242f
7 changed files with 110 additions and 1 deletions
@@ -17,6 +17,18 @@ public class JdbcTxManager implements TxManager {
this.ds = Objects.requireNonNull(ds);
}
/** Closes the data source when it is closeable, as a pool is; a plain {@code DataSource} holds nothing to release. */
@Override
public void close() {
if (ds instanceof AutoCloseable closeable) {
try {
closeable.close();
} catch (Exception e) {
throw new IllegalStateException("Failed to close the data source", e);
}
}
}
@Override
public TxStatus begin(TxDefinition definition) {
return switch (definition.propagation()) {
@@ -0,0 +1,20 @@
package dev.relism.flash.ext.data.jdbc;
import com.zaxxer.hikari.HikariDataSource;
import dev.relism.flash.ext.data.DataExtension;
import dev.relism.flash.extension.FlashApp;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
class JdbcTxManagerCloseTest {
/** A pool outlives its app unless someone closes it; the data extension does, once requests have drained. */
@Test
void stoppingTheAppClosesThePool() {
HikariDataSource pool = new HikariDataSource();
pool.setJdbcUrl(TestDataSource.URL);
FlashApp.create(0).install(new DataExtension(new JdbcTxManager(pool))).start().stop().join();
assertTrue(pool.isClosed());
}
}