Tomcat 8 global DataSource closed before `ServletContextListener.contextDestroyed ()`
I have deployed a web application to Tomcat 8. This application defines a global JNDI DataSource
that is shared by multiple servlets. Inside the application, it is wrapped in a Spring bean that uses a factory method to retrieve this from the JNDI. There is also ServletContextListener
one implemented to react when the web application context is destroyed. The application itself works great and DataSource
can be retrieved and used correctly.
Problem . On completion, Tomcat ( catalina.sh stop
, SIGINT
or SIGTERM
) is DataSource
closed before being called ServletContextListener.contextDestroyed()
. This is bad because I need to access the DB in this method (for example, to properly close our Quartz scheduler, which has access to the database).
Here's a snippet server.xml
:
<Server port="8005" shutdown="SHUTDOWN">
...
<GlobalNamingResources>
...
<Resource name="myDB" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000" username="" password=""
removeAbandonedOnBorrow="true" removeAbandonedTimeout="60" driverClassName="org.h2.Driver"
url="jdbc:h2:myApp" defaultTransactionIsolation="READ_COMMITTED" />
...
</GlobalNamingResources>
...
</Server>
Here from context.xml
:
<Context path="/myApp" docBase="myApp"
reloadable="false" crossContext="false" antiResourceLocking="true" unloadDelay="5000">
<ResourceLink name="jdbc/myDB" global="myDB" type="javax.sql.DataSource" />
<Manager className="org.apache.catalina.session.PersistentManager"
distributable="false" saveOnRestart="false">
<Store className="org.apache.catalina.session.FileStore"/>
</Manager>
</Context>
Here is a snippet of mine ServletContextListener
:
public class MyListener implements ServletContextListener {
...
@Override
public void contextDestroyed(ServletContextEvent sce) {
...
// using the DataSource here
...
}
...
}
In the logs I see Exception
when I try to use DataSource
in ServletContextListener
, saying that the datasource is already closed:
05 Aug 2015 14:59:17,915 [localhost-startStop-2] () || DEBUG DisposableBeanAdapter: Invoking destroy method 'close' on bean with name 'dataSource'
05-Aug-2015 14:59:17.918 WARNING [localhost-startStop-2] org.apache.tomcat.dbcp.dbcp2.BasicDataSource.close Failed to unregister the JMX name: Catalina:type=DataSource,class=javax.sql.DataSource,name="myDB"
javax.management.InstanceNotFoundException: Catalina:type=DataSource,class=javax.sql.DataSource,name="myDB"
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(Unknown Source)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.exclusiveUnregisterMBean(Unknown Source)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.unregisterMBean(Unknown Source)
at com.sun.jmx.mbeanserver.JmxMBeanServer.unregisterMBean(Unknown Source)
at org.apache.tomcat.dbcp.dbcp2.BasicDataSource.close(BasicDataSource.java:1916)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:327)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:253)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:510)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:742)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:455)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1090)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1064)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1010)
at org.springframework.web.context.ContextLoader.closeWebApplicationContext(ContextLoader.java:586)
at org.springframework.web.context.ContextLoaderListener.contextDestroyed(ContextLoaderListener.java:143)
at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4776)
at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5390)
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1424)
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1413)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
...
Shutting down scheduler.
...
05 Aug 2015 14:59:24,098 [QuartzScheduler_DefaultQuartzScheduler-4424f282be6c1438786703813_ClusterManager] () || ERROR JobStoreTX : ClusterManager: Error managing cluster: Failed to obtain DB connection from data source 'qrtzds': java.sql.SQLException: Could not retrieve datasource via JNDI url 'java:comp/env/jdbc/myDB' java.sql.SQLException: Data source is closed
org.quartz.JobPersistenceException: Failed to obtain DB connection from data source 'qrtzds': java.sql.SQLException: Could not retrieve datasource via JNDI url 'java:comp/env/jdbc/myDB' java.sql.SQLException: Data source is closed [See nested exception: java.sql.SQLException: Could not retrieve datasource via JNDI url 'java:comp/env/jdbc/myDB' java.sql.SQLException: Data source is closed]
at org.quartz.impl.jdbcjobstore.JobStoreSupport.getConnection(JobStoreSupport.java:778)
at org.quartz.impl.jdbcjobstore.JobStoreTX.getNonManagedTXConnection(JobStoreTX.java:71)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.doCheckin(JobStoreSupport.java:3245)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$ClusterManager.manage(JobStoreSupport.java:3858)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$ClusterManager.run(JobStoreSupport.java:3895)
Caused by: java.sql.SQLException: Could not retrieve datasource via JNDI url 'java:comp/env/jdbc/myDB' java.sql.SQLException: Data source is closed
at org.quartz.utils.JNDIConnectionProvider.getConnection(JNDIConnectionProvider.java:163)
at org.quartz.utils.DBConnectionManager.getConnection(DBConnectionManager.java:108)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.getConnection(JobStoreSupport.java:775)
... 4 more
Question
- How can I manage the Tomcat lifecycle to close the global resource
DataSource
after destroying the web application context? - How can I tell Spring not to use a method
BasicDataSource.close()
? I tried to set the bean attributedestroy-method
to empty. But it seems that when it is empty, it still calls the methodclose()
because it detectsAutoClosable
.
source to share
No one has answered this question yet
Check out similar questions: