001package io.ebean.event; 002 003import io.ebean.Database; 004import io.ebean.service.SpiContainer; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008import java.sql.Driver; 009import java.sql.DriverManager; 010import java.sql.SQLException; 011import java.util.ArrayList; 012import java.util.Enumeration; 013import java.util.List; 014import java.util.concurrent.locks.ReentrantLock; 015 016/** 017 * Manages the shutdown of Ebean. 018 * <p> 019 * Makes sure all the resources are shutdown properly and in order. 020 */ 021public final class ShutdownManager { 022 023 private static final Logger log = LoggerFactory.getLogger("io.ebean"); 024 private static final ReentrantLock lock = new ReentrantLock(); 025 private static final List<Database> databases = new ArrayList<>(); 026 private static final ShutdownHook shutdownHook = new ShutdownHook(); 027 028 private static boolean stopping; 029 private static SpiContainer container; 030 static { 031 // Register the Shutdown hook 032 registerShutdownHook(); 033 } 034 035 /** 036 * Disallow construction. 037 */ 038 private ShutdownManager() { 039 } 040 041 /** 042 * Registers the container (potentially with cluster management). 043 */ 044 public static void registerContainer(SpiContainer ebeanContainer) { 045 container = ebeanContainer; 046 } 047 048 /** 049 * Make sure the ShutdownManager is activated. 050 */ 051 public static void touch() { 052 // Do nothing 053 } 054 055 /** 056 * Return true if the system is in the process of stopping. 057 */ 058 public static boolean isStopping() { 059 lock.lock(); 060 try { 061 return stopping; 062 } finally { 063 lock.unlock(); 064 } 065 } 066 067 /** 068 * Deregister the Shutdown hook. 069 * <p> 070 * In calling this method it is expected that application code will invoke 071 * the shutdown() method. 072 * </p> 073 * <p> 074 * For running in a Servlet Container a redeploy will cause a shutdown, and 075 * for that case we need to make sure the shutdown hook is deregistered. 076 * </p> 077 */ 078 public static void deregisterShutdownHook() { 079 lock.lock(); 080 try { 081 Runtime.getRuntime().removeShutdownHook(shutdownHook); 082 } catch (IllegalStateException ex) { 083 if (!ex.getMessage().equals("Shutdown in progress")) { 084 throw ex; 085 } 086 } finally { 087 lock.unlock(); 088 } 089 } 090 091 /** 092 * Register the shutdown hook with the Runtime. 093 */ 094 private static void registerShutdownHook() { 095 lock.lock(); 096 try { 097 String value = System.getProperty("ebean.registerShutdownHook"); 098 if (value == null || !value.trim().equalsIgnoreCase("false")) { 099 Runtime.getRuntime().addShutdownHook(shutdownHook); 100 } 101 } catch (IllegalStateException ex) { 102 if (!ex.getMessage().equals("Shutdown in progress")) { 103 throw ex; 104 } 105 } finally { 106 lock.unlock(); 107 } 108 } 109 110 /** 111 * Shutdown gracefully cleaning up any resources as required. 112 * <p> 113 * This is typically invoked via JVM shutdown hook. 114 * </p> 115 */ 116 public static void shutdown() { 117 lock.lock(); 118 try { 119 if (stopping) { 120 // Already run shutdown... 121 return; 122 } 123 if (log.isDebugEnabled()) { 124 log.debug("Ebean shutting down"); 125 } 126 stopping = true; 127 deregisterShutdownHook(); 128 129 String shutdownRunner = System.getProperty("ebean.shutdown.runnable"); 130 if (shutdownRunner != null) { 131 try { 132 // A custom runnable executed at the start of shutdown 133 Runnable r = (Runnable) ClassUtil.newInstance(shutdownRunner); 134 r.run(); 135 } catch (Exception e) { 136 log.error("Error running custom shutdown runnable", e); 137 } 138 } 139 140 if (container != null) { 141 // shutdown cluster networking if active 142 container.shutdown(); 143 } 144 // shutdown any registered servers that have not 145 // already been shutdown manually 146 for (Database server : databases) { 147 try { 148 server.shutdown(); 149 } catch (Exception ex) { 150 log.error("Error executing shutdown runnable", ex); 151 ex.printStackTrace(); 152 } 153 } 154 if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) { 155 deregisterAllJdbcDrivers(); 156 } 157 } finally { 158 lock.unlock(); 159 } 160 } 161 162 private static void deregisterAllJdbcDrivers() { 163 // This manually de-registers all JDBC drivers 164 Enumeration<Driver> drivers = DriverManager.getDrivers(); 165 while (drivers.hasMoreElements()) { 166 Driver driver = drivers.nextElement(); 167 try { 168 log.info("De-registering jdbc driver: " + driver); 169 DriverManager.deregisterDriver(driver); 170 } catch (SQLException e) { 171 log.error("Error de-registering driver " + driver, e); 172 } 173 } 174 } 175 176 /** 177 * Register an ebeanServer to be shutdown when the JVM is shutdown. 178 */ 179 public static void registerDatabase(Database server) { 180 lock.lock(); 181 try { 182 databases.add(server); 183 } finally { 184 lock.unlock(); 185 } 186 } 187 188 /** 189 * Deregister an ebeanServer. 190 * <p> 191 * This is done when the ebeanServer is shutdown manually. 192 * </p> 193 */ 194 public static void unregisterDatabase(Database server) { 195 lock.lock(); 196 try { 197 databases.remove(server); 198 } finally { 199 lock.unlock(); 200 } 201 } 202 203 private static class ShutdownHook extends Thread { 204 private ShutdownHook() { 205 super("EbeanHook"); 206 } 207 @Override 208 public void run() { 209 ShutdownManager.shutdown(); 210 } 211 } 212}