From 32979536e67eb39a8bef268f9b15cf398377ac9d Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Mon, 9 Mar 2015 16:01:48 -0400 Subject: [PATCH] VIVO-987 Properly shutdown the TripleSource instances. When shutting down a ContentTripleSourceSDB instance, attempt to deregister the JDBC driver. Also, attempt to shut down the AbandonedConnectionCleanupThread. Each of these produce warning messages if not shut down. --- .../webapp/application/ApplicationImpl.java | 6 +- .../impl/jena/tdb/RDFServiceTDB.java | 8 +-- .../impl/sdb/ContentTripleSourceSDB.java | 59 ++++++++++++++++++- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java index 2ab2776a2..36e230466 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java @@ -280,9 +280,11 @@ public class ApplicationImpl implements Application { @Override public void contextDestroyed(ServletContextEvent sce) { Application app = ApplicationUtils.instance(); - app.getSearchEngine().shutdown(app); - app.getImageProcessor().shutdown(app); + app.getConfigurationTripleSource().shutdown(app); + app.getContentTripleSource().shutdown(app); app.getFileStorage().shutdown(app); + app.getImageProcessor().shutdown(app); + app.getSearchEngine().shutdown(app); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java index f611b9c48..0ad42b055 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/tdb/RDFServiceTDB.java @@ -16,6 +16,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.hp.hpl.jena.query.Dataset; +import com.hp.hpl.jena.shared.JenaException; import com.hp.hpl.jena.shared.Lock; import com.hp.hpl.jena.tdb.TDB; import com.hp.hpl.jena.tdb.TDBFactory; @@ -94,12 +95,7 @@ public class RDFServiceTDB extends RDFServiceJena { @Override public void close() { if (this.dataset != null) { - dataset.getLock().enterCriticalSection(Lock.WRITE); - try { - dataset.close(); - } finally { - dataset.getLock().leaveCriticalSection(); - } + dataset.close(); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/ContentTripleSourceSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/ContentTripleSourceSDB.java index 68187631b..d89ca29b7 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/ContentTripleSourceSDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/ContentTripleSourceSDB.java @@ -4,8 +4,12 @@ package edu.cornell.mannlib.vitro.webapp.triplesource.impl.sdb; import static edu.cornell.mannlib.vitro.webapp.triplesource.impl.BasicCombinedTripleSource.CONTENT_UNIONS; +import java.lang.reflect.Method; +import java.sql.Driver; +import java.sql.DriverManager; import java.sql.SQLException; import java.util.Arrays; +import java.util.Enumeration; import javax.servlet.ServletContext; import javax.sql.DataSource; @@ -76,7 +80,7 @@ public class ContentTripleSourceSDB extends ContentTripleSource { static final boolean DEFAULT_TESTONBORROW = true; static final boolean DEFAULT_TESTONRETURN = true; - + private ServletContext ctx; private ComboPooledDataSource ds; private RDFServiceFactory rdfServiceFactory; @@ -212,9 +216,62 @@ public class ContentTripleSourceSDB extends ContentTripleSource { @Override public void shutdown(Application application) { + if (this.modelMaker != null) { + this.modelMaker.close(); + } + if (this.dataset != null) { + this.dataset.close(); + } + if (this.rdfService != null) { + this.rdfService.close(); + } if (ds != null) { + String driverClassName = ds.getDriverClass(); ds.close(); + attemptToDeregisterJdbcDriver(driverClassName); + cleanupAbandonedConnectionThread(driverClassName); } } + private void attemptToDeregisterJdbcDriver(String driverClassName) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + for (Enumeration drivers = DriverManager.getDrivers(); drivers + .hasMoreElements();) { + Driver driver = drivers.nextElement(); + if (driver.getClass().getClassLoader() == cl) { + // This driver was registered by the webapp's ClassLoader, so + // deregister it: + try { + DriverManager.deregisterDriver(driver); + } catch (SQLException ex) { + log.error("Error deregistering JDBC driver {" + driver + + "}", ex); + } + } else { + // driver was not registered by the webapp's ClassLoader and may + // be in use elsewhere + } + } + } + + /** + * The MySQL driver leaves a thread running after it is deregistered. + * Versions after 5.1.23 provide AbandonedConnectionCleanupThread.shutdown() + * to stop this thread. + * + * Using reflection to invoke this method means that we don't have a + * hard-coded dependency to MySQL. + */ + private void cleanupAbandonedConnectionThread(String driverClassName) { + if (!driverClassName.contains("mysql")) { + return; + } + try { + Class.forName("com.mysql.jdbc.AbandonedConnectionCleanupThread") + .getMethod("shutdown").invoke(null); + } catch (Exception e) { + log.info("Failed to shutdown MySQL connection cleanup thread: " + e); + } + } }