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 attribute destroy-method

    to empty. But it seems that when it is empty, it still calls the method close()

    because it detectsAutoClosable

    .
+3


source to share





All Articles