/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.core.spring; import javax.servlet.ServletContextListener; import javax.servlet.ServletContextEvent; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Enumeration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** A simple bean that performs memory clean up of the application when the * spring context shuts down. * * This class is intended to be used as a spring bean. Without this, java * leaves some references to classes in the web context class loader when the * web application context shuts down. This makes it impossible to hot-redeploy * the web application multiple times, eventualy leading to an out of perm gen * memory. * * There are various causes for this. * * The current implementation: * * Frees the jdbc drivers registered in the driver manager. * * @author pablo.saavedra */ public class ReferenceCleaner implements ServletContextListener { /** The class logger. */ private static Logger log = LoggerFactory.getLogger(ReferenceCleaner.class); /** This method does nothing. * * {@inheritDoc} */ public void contextInitialized(final ServletContextEvent event) { // noting to do. } /** Performs the memory clean up when the context destroyed event is * received. * * Basic clean up done by this method is to de-register the JDBC drivers from * the {@link DriverManager}. * * @param event This is the event class for notifications about changes to * the servlet context of a web application. This parameter is ignored. * * {@inheritDoc} */ public void contextDestroyed(final ServletContextEvent event) { log.trace("Entering contextDestroyed"); deregisterJdbcDrivers(); // shutDownEvictorThreads(); log.trace("Leaving contextDestroyed"); } /** Deregisters the jdbc drivers from the driver manager. * * This is necessary so that the garbage collector can free the war class * loader when the war is redeployed. The driver manager in the system class * loader holds a reference to the jdbc driver in the war class loader. */ private void deregisterJdbcDrivers() { log.trace("Entering deregisterJdbcDrivers"); ClassLoader myClassLoader = getClass().getClassLoader(); if (DriverManager.class.getClassLoader() == myClassLoader) { // The driver manager and this class share the same classloader. There is // no need to deregister the driver from the driver manager. log.trace("Leaving deregisterJdbcDrivers"); return; } try { Enumeration<Driver> e = DriverManager.getDrivers(); while (e.hasMoreElements()) { Driver driver = e.nextElement(); if (driver.getClass().getClassLoader() == myClassLoader) { if (log.isDebugEnabled()) { log.debug("Deregistering jdbc driver " + driver.getClass()); } DriverManager.deregisterDriver(driver); } } } catch (SQLException e) { log.error("Error deregistering jdbc driver", e); throw new RuntimeException("Unable to deregister drivers", e); } log.trace("Leaving deregisterJdbcDrivers"); } }