You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by mr...@apache.org on 2026/10/20 05:41:02 UTC
(ofbiz-framework) 01/02: Fixed: Close the embedded H2 database cleanly on shutdown (OFBIZ-13686)
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 5d8d7924f8851d3eae0cf93c068676182e965822
Author: Mridul Pathak <mr...@gmail.com>
AuthorDate: Thu Oct 15 19:19:46 2026 +0530
Fixed: Close the embedded H2 database cleanly on shutdown (OFBIZ-13686)
OFBiz never closed its datasources, so with DB_CLOSE_ON_EXIT=FALSE every exit was an unclean close for the embedded H2 database, after which H2 can silently lose committed data on the next open.
- DBCPConnectionFactory.closeAll() now closes the connection pools instead of only clearing its cache, so that H2 closes the database once its last connection is closed.
- The new datasource-container of the entity component, loaded for every loader, shuts down the transaction factory, and so closes the pools, when it is stopped; being declared before the other containers, it is stopped after them.
- ContainerLoader adds each container to the loaded containers as soon as it is initialized, as described in the Container javadoc, and keeps stopping the other containers when one throws a RuntimeException.
- Start stops the loaded containers when OFBiz fails to start, since the shutdown hook is disabled for the load-data and test runs.
---
.../ofbiz/base/container/ContainerLoader.java | 15 +++---
framework/entity/ofbiz-component.xml | 2 +
.../apache/ofbiz/entity/DataSourceContainer.java | 61 ++++++++++++++++++++++
.../entity/connection/DBCPConnectionFactory.java | 10 +++-
.../java/org/apache/ofbiz/base/start/Start.java | 2 +
5 files changed, 81 insertions(+), 9 deletions(-)
diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/container/ContainerLoader.java b/framework/base/src/main/java/org/apache/ofbiz/base/container/ContainerLoader.java
index d0db7c4b32..ba7decfaa5 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/container/ContainerLoader.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/container/ContainerLoader.java
@@ -18,7 +18,6 @@
*******************************************************************************/
package org.apache.ofbiz.base.container;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
@@ -71,7 +70,7 @@ public class ContainerLoader {
// Load containers defined in components.
Debug.logInfo("[Startup] Loading containers...", MODULE);
- loadedContainers.addAll(loadContainersFromConfigurations(config.getLoaders(), ofbizCommands));
+ loadContainersFromConfigurations(config.getLoaders(), ofbizCommands);
// Start all containers loaded from above steps
startLoadedContainers();
@@ -90,23 +89,22 @@ public class ContainerLoader {
/**
* Loads the available containers which are matching the configured loaders.
+ * Each container is added to the loaded containers as soon as it is initialized, so that it
+ * gets stopped on shutdown even when a container loaded after it fails to load.
* @param loaders the collection of loaders to match
* @param ofbizCommands the parsed commands line arguments used by the containers
- * @return a list of loaded containers.
* @throws StartupException when a container fails to load.
*/
- private static List<Container> loadContainersFromConfigurations(Collection<String> loaders,
+ private void loadContainersFromConfigurations(Collection<String> loaders,
List<StartupCommand> ofbizCommands) throws StartupException {
- List<Container> loadContainers = new ArrayList<>();
for (ContainerConfig.Configuration containerCfg : ComponentConfig.getAllConfigurations()) {
if (intersects(containerCfg.loaders(), loaders)) {
Debug.logInfo("Loading container: " + containerCfg.name(), MODULE);
Container tmpContainer = loadContainer(containerCfg, ofbizCommands);
- loadContainers.add(tmpContainer);
+ loadedContainers.add(tmpContainer);
Debug.logInfo("Loaded container: " + containerCfg.name(), MODULE);
}
}
- return loadContainers;
}
@@ -167,7 +165,8 @@ public class ContainerLoader {
Debug.logInfo("Stopping container " + loadedContainer.getName(), MODULE);
try {
loadedContainer.stop();
- } catch (ContainerException e) {
+ } catch (ContainerException | RuntimeException e) {
+ // keep stopping the other containers, the last ones close the datasources
Debug.logError(e, MODULE);
}
Debug.logInfo("Stopped container " + loadedContainer.getName(), MODULE);
diff --git a/framework/entity/ofbiz-component.xml b/framework/entity/ofbiz-component.xml
index e2bd5adf8c..70f618fb45 100644
--- a/framework/entity/ofbiz-component.xml
+++ b/framework/entity/ofbiz-component.xml
@@ -32,6 +32,8 @@ under the License.
<test-suite loader="main" location="testdef/entitytests.xml"/>
+ <!-- declared first so it is stopped last, once every other container has stopped using the datasources -->
+ <container name="datasource-container" loaders="main,rmi,load-data,test" class="org.apache.ofbiz.entity.DataSourceContainer"/>
<container name="delegator-container" loaders="main" class="org.apache.ofbiz.entity.DelegatorContainer">
<property name="preload-delegators" value="default"/>
</container>
diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/DataSourceContainer.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/DataSourceContainer.java
new file mode 100644
index 0000000000..8600b843b2
--- /dev/null
+++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/DataSourceContainer.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.ofbiz.entity;
+
+import java.util.List;
+
+import org.apache.ofbiz.base.container.Container;
+import org.apache.ofbiz.base.start.StartupCommand;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.entity.transaction.TransactionFactoryLoader;
+
+/**
+ * Closes the entity engine datasources when OFBiz shuts down.
+ * <p>
+ * An embedded database such as H2 must be closed for its files to be left in a consistent state: on the next
+ * open after an unclean close, H2 can silently lose committed data. This container is loaded before the others
+ * so that it is stopped last, after every container that may still use the datasources.
+ */
+public class DataSourceContainer implements Container {
+
+ private static final String MODULE = DataSourceContainer.class.getName();
+
+ private String name;
+
+ @Override
+ public void init(List<StartupCommand> ofbizCommands, String name, String configFile) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean start() {
+ return true;
+ }
+
+ @Override
+ public void stop() {
+ Debug.logInfo("Closing the datasources", MODULE);
+ TransactionFactoryLoader.getInstance().shutdown();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
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 98e2759d5b..ecad030d93 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
@@ -170,7 +170,15 @@ public class DBCPConnectionFactory implements ConnectionFactory {
@Override
public void closeAll() {
- // no methods on the pool to shutdown; so just clearing for GC
+ // 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) -> {
+ try {
+ mds.close();
+ } catch (SQLException e) {
+ Debug.logError(e, "Error closing the connection pool for " + helperName, MODULE);
+ }
+ });
DS_CACHE.clear();
}
diff --git a/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java b/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java
index 62a6765146..33ed1dfb26 100644
--- a/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java
+++ b/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java
@@ -88,6 +88,8 @@ public final class Start {
try {
StartupControlPanel.start(INSTANCE.config, INSTANCE.serverState, ofbizCommands, INSTANCE.loader);
} catch (StartupException e) {
+ // stop what was loaded, the shutdown hook being possibly disabled
+ StartupControlPanel.shutdownServer(INSTANCE.loader, INSTANCE.serverState);
StartupControlPanel.fullyTerminateSystem(e);
}
break;