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