From eeacad0fb987735a82c45b0c7788232900f27bbb Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 12 Aug 2021 14:36:14 +1200 Subject: [PATCH] #2307 - ENH: Add DatabaseFactory.disableShutdownHook() ... [ alias for ShutdownManager.deregisterShutdownHook() ] --- .../main/java/io/ebean/DatabaseFactory.java | 23 +++++++-- .../java/io/ebean/event/ShutdownManager.java | 22 ++++----- .../server/lib/ShutdownManagerTest.java | 48 +++++++++++++++++++ 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java index 4aace866a..8dbf05fd0 100644 --- a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java +++ b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java @@ -2,6 +2,7 @@ package io.ebean; import io.ebean.config.ContainerConfig; import io.ebean.config.DatabaseConfig; +import io.ebean.event.ShutdownManager; import io.ebean.service.SpiContainer; import io.ebean.service.SpiContainerFactory; @@ -16,18 +17,15 @@ import java.util.concurrent.locks.ReentrantLock; *

* This uses either DatabaseConfig or properties in the application.properties file to * configure and create a Database instance. - *

*

* The Database instance can either be registered with the DB singleton or * not. The DB singleton effectively holds a map of Database by a name. * If the Database is registered with the DB singleton you can retrieve it * later via {@link DB#byName(String)}. - *

*

* One Database can be nominated as the 'default/primary' Database. Many * methods on the DB singleton such as {@link DB#find(Class)} are just a * convenient way of using the 'default/primary' Database. - *

*/ public class DatabaseFactory { @@ -68,6 +66,16 @@ public class DatabaseFactory { /** * Create using the DatabaseConfig object to configure the database. + * + *
{@code
+   *
+   *   DatabaseConfig config = new DatabaseConfig();
+   *   config.setName("db");
+   *   config.loadProperties();
+   *
+   *   Database database = DatabaseFactory.create(config);
+   *
+   * }
*/ public static Database create(DatabaseConfig config) { lock.lock(); @@ -115,7 +123,6 @@ public class DatabaseFactory { * Shutdown gracefully all Database instances cleaning up any resources as required. *

* This is typically invoked via JVM shutdown hook and not explicitly called. - *

*/ public static void shutdown() { lock.lock(); @@ -126,6 +133,14 @@ public class DatabaseFactory { } } + /** + * Removes the JVM shutdown hook and means the application must shut down ebean + * explicitly using {@link DatabaseFactory#shutdown()}. + */ + public static void disableShutdownHook() { + ShutdownManager.deregisterShutdownHook(); + } + private static Database createInternal(DatabaseConfig config) { return getContainer(config.getContainerConfig()).createServer(config); } diff --git a/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java b/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java index f762b0084..4011683ca 100644 --- a/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java +++ b/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java @@ -14,10 +14,9 @@ import java.util.List; import java.util.concurrent.locks.ReentrantLock; /** - * Manages the shutdown of the JVM Runtime. + * Manages the shutdown of Ebean. *

* Makes sure all the resources are shutdown properly and in order. - *

*/ public final class ShutdownManager { @@ -44,6 +43,9 @@ public final class ShutdownManager { private ShutdownManager() { } + /** + * Registers the container (potentially with cluster management). + */ public static void registerContainer(SpiContainer ebeanContainer) { container = ebeanContainer; } @@ -94,7 +96,7 @@ public final class ShutdownManager { /** * Register the shutdown hook with the Runtime. */ - protected static void registerShutdownHook() { + private static void registerShutdownHook() { lock.lock(); try { String value = System.getProperty("ebean.registerShutdownHook"); @@ -123,13 +125,10 @@ public final class ShutdownManager { // Already run shutdown... return; } - if (logger.isDebugEnabled()) { logger.debug("Shutting down"); } - stopping = true; - deregisterShutdownHook(); String shutdownRunner = System.getProperty("ebean.shutdown.runnable"); @@ -147,7 +146,6 @@ public final class ShutdownManager { // shutdown cluster networking if active container.shutdown(); } - // shutdown any registered servers that have not // already been shutdown manually for (Database server : databases) { @@ -158,7 +156,6 @@ public final class ShutdownManager { ex.printStackTrace(); } } - if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) { deregisterAllJdbcDrivers(); } @@ -168,15 +165,15 @@ public final class ShutdownManager { } private static void deregisterAllJdbcDrivers() { - // This manually deregisters all JDBC drivers + // This manually de-registers all JDBC drivers Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); try { - logger.info("Deregistering jdbc driver: " + driver); + logger.info("De-registering jdbc driver: " + driver); DriverManager.deregisterDriver(driver); } catch (SQLException e) { - logger.error("Error deregistering driver " + driver, e); + logger.error("Error de-registering driver " + driver, e); } } } @@ -209,6 +206,9 @@ public final class ShutdownManager { } private static class ShutdownHook extends Thread { + private ShutdownHook() { + super("EbeanHook"); + } @Override public void run() { ShutdownManager.shutdown(); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/lib/ShutdownManagerTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/lib/ShutdownManagerTest.java index e5f8c3853..f643597ed 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/lib/ShutdownManagerTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/lib/ShutdownManagerTest.java @@ -2,12 +2,60 @@ package io.ebeaninternal.server.lib; import io.ebean.BaseTestCase; import io.ebean.DB; +import io.ebean.Database; +import io.ebean.DatabaseFactory; import io.ebean.event.ShutdownManager; import org.junit.Ignore; import org.junit.Test; public class ShutdownManagerTest extends BaseTestCase { + /** + * Run this test manually. Most typical when we want the application code to control shutdown. + */ + @Ignore + @Test + public void test_disableShutdownHook_shutdownManually() { + // disable hook to make sure app code controls when shutdown is executed + DatabaseFactory.disableShutdownHook(); + DB.getDefault(); + + System.out.println("shutdown manually ... "); + // application code explicitly calls shutdown() + DatabaseFactory.shutdown(); + } + + /** + * Run this test manually. + */ + @Ignore + @Test + public void test_shutdownHook() { + DB.getDefault(); // shutdown fired via shutdown hook, default behaviour + } + + /** + * Run this test manually. + */ + @Ignore + @Test + public void test_disableShutdownHook() { + DB.getDefault(); + DatabaseFactory.disableShutdownHook(); // no shutdown is run here (not great, don't do this) + } + + /** + * Run this test manually. + */ + @Ignore + @Test + public void test_shutdownManually() { + DB.getDefault(); + System.out.println("shutdown manually ... "); + // note this removes the shutdown hook, only "useful" if it runs BEFORE a JVM shutdown is invoked (hook invoked) + DatabaseFactory.shutdown(); + } + /** * Run this test manually. */