This is an automated email from the ASF dual-hosted git repository. mridulpathak pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
commit bccb166adeca4e2a6f1e5a06d388c1c4bf302eda Author: Mridul Pathak <[email protected]> AuthorDate: Mon Oct 19 21:35:55 2026 +0530 Improved: Prevent the datasources from being reopened during shutdown (OFBIZ-13686) A thread still running after the datasources are closed can no longer reopen the embedded H2 database and leave it unclosed. - DBCPConnectionFactory.getConnection() fails once closeAll() has run, instead of creating a new pool. - JobPoller.stop() waits up to 5 seconds for the interrupted jobs to end and return their connections before the datasources get closed. - DOCKER.adoc warns that H2 is not closed when docker stop kills the container before OFBiz has finished shutting down, and how to give it more time. Thanks: Jacopo Cappellato (jacopoc) for the review and suggestion --- DOCKER.adoc | 7 +++++++ .../ofbiz/entity/connection/DBCPConnectionFactory.java | 12 ++++++++++++ .../main/java/org/apache/ofbiz/service/job/JobPoller.java | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/DOCKER.adoc b/DOCKER.adoc index 001ecd49b7..d7bb598986 100644 --- a/DOCKER.adoc +++ b/DOCKER.adoc @@ -169,6 +169,13 @@ variables. To use the embedded H2 database, ensure all database related environment variables are unset. +OFBiz closes the H2 database at the end of its shutdown. `+docker stop+` +kills the container if it has not stopped within 10 seconds, in which case +H2 is not closed and can lose recently committed data the next time it is +opened. If the shutdown can take longer, for instance while jobs are still +running, give the container more time with `+docker stop -t <seconds>+`, or +`+stop_grace_period+` with Docker Compose. + ===== PostgreSQL To use a Postgres database set the `+OFBIZ_POSTGRES_HOST+` environment diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/connection/DBCPConnectionFactory.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/connection/DBCPConnectionFactory.java index ecad030d93..0212b57649 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/connection/DBCPConnectionFactory.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/connection/DBCPConnectionFactory.java @@ -58,10 +58,15 @@ public class DBCPConnectionFactory implements ConnectionFactory { // from DebugManagedDataSource protected static final ConcurrentHashMap<String, DebugManagedDataSource<? extends Connection>> DS_CACHE = new ConcurrentHashMap<>(); + // set by closeAll() when OFBiz shuts down, so that no new pool gets created afterwards (see OFBIZ-13686) + private static volatile boolean closed = false; @SuppressWarnings("deprecation") @Override public Connection getConnection(GenericHelperInfo helperInfo, JdbcElement abstractJdbc) throws SQLException, GenericEntityException { + if (closed) { + throw new SQLException("The datasources have been closed, OFBiz is shutting down"); + } String cacheKey = helperInfo.getHelperFullName(); DebugManagedDataSource<? extends Connection> mds = DS_CACHE.get(cacheKey); if (mds != null) { @@ -164,12 +169,19 @@ public class DBCPConnectionFactory implements ConnectionFactory { // cache the pool DS_CACHE.putIfAbsent(cacheKey, mds); mds = DS_CACHE.get(cacheKey); + if (closed) { + // closeAll() ran while this pool was being created + DS_CACHE.remove(cacheKey, mds); + mds.close(); + throw new SQLException("The datasources have been closed, OFBiz is shutting down"); + } return TransactionUtil.getCursorConnection(helperInfo, mds.getConnection()); } @Override public void closeAll() { + closed = true; // Closing the pools closes every pooled connection, which is what lets an embedded database such as H2 // close its files cleanly: H2 only does so when its last connection is closed (see OFBIZ-13686). DS_CACHE.forEach((helperName, mds) -> { diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/job/JobPoller.java b/framework/service/src/main/java/org/apache/ofbiz/service/job/JobPoller.java index 7b1b55f584..c41e9022f2 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/job/JobPoller.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/job/JobPoller.java @@ -52,6 +52,7 @@ public final class JobPoller implements ServiceConfigListener { private static final ConcurrentHashMap<String, JobManager> JOB_MANAGERS = new ConcurrentHashMap<>(); private static final ThreadPoolExecutor EXECUTOR = createThreadPoolExecutor(); private static final JobPoller INSTANCE = new JobPoller(); + private static final long SHUTDOWN_TIMEOUT_SECONDS = 5; /** * Returns the <code>JobPoller</code> instance. @@ -252,6 +253,15 @@ public final class JobPoller implements ServiceConfigListener { Debug.logWarning(e, MODULE); } } + // Give the interrupted jobs a short time to end and return their connections before the datasources get closed + // (see OFBIZ-13686); the wait is kept short so that a container runtime does not kill OFBiz before that + try { + if (!EXECUTOR.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + Debug.logWarning("Some jobs were still running after " + SHUTDOWN_TIMEOUT_SECONDS + " seconds", MODULE); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } Debug.logInfo("JobPoller shutdown completed.", MODULE); }

