3

I have a spring boot service using spring-boot 1.2.7 deploying to Tomcat 8 using Java 8. The service runs fine on a fresh reboot of the Tomcat server, but after undeploy and redeploy I get the 'Data source is closed' error.

UPDATE this does work on tomcat 7 java 7. Hey all, I've created a simple sample application that is getting the error and using an H2 jndi datasource here. https://github.com/dstrimble/ws_preRatesFilter I've placed the tomcat config files in the project base dir

I am using a JNDI datasource on an external Tomcat service, not embedded.

sql.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

    <util:properties id="preRatesFilterQueriesBean">
        <prop key="GET_SPECIFIC_FILTER_DATA">
            <![CDATA[
                SELECT ...
            ]]>
        </prop>

        <prop key="GET_ALL_FILTER_DATA">
            <![CDATA[
                SELECT ...
            ]]>
        </prop>
    </util:properties>
</beans>

WsPreRatesFilterApplication.java

@ImportResource({"classpath:sql.xml"})
public class WsPreRatesFilterApplication extends SpringBootServletInitializer {

    private final Logger logger = LoggerFactory.getLogger(WsPreRatesFilterApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(WsPreRatesFilterApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        logger.info("Inside SpringApplicationBuilder configure");
        return application.sources(WsPreRatesFilterApplication.class);
    }
}

application.properties

spring.datasource.jndi-name=com.datasource.JBHDB2P
server.context-path=/ws_preRatesFilter
info.app.name=ws_preRatesFilter
info.app.description=Simple Boot Data Service
info.app.version=1.0.0

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com</groupId>
    <artifactId>ws_preRatesFilter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>ws_preRatesFilter</name>
    <description>filter rate input before rates called</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>com.ws.preratesfilter.WsPreRatesFilterApplication</start-class>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

The error is:

{
  "timestamp": 1445347159226,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "org.springframework.jdbc.CannotGetJdbcConnectionException",
  "message": "Could not get JDBC Connection; nested exception is java.sql.SQLException: Data source is closed",
  "path": "/ws_preRatesFilter/fetchFilterData"
}

The full stack trace is:

2015/10/20 08:19:19.216 ERROR o.s.b.c.w.ErrorPageFilter - Forwarding to error page from request [/fetchFilterData] due to exception [Could not get JDBC Connection; nested exception is java.sql.SQLException: Data source is closed]
org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Data source is closed
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:630) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:695) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:722) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:772) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:192) ~[spring-jdbc-4.1.8.RELEASE.jar:na]
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:199) ~[spring-jdbc-4.1.8.RELEASE.jar:na]
    at com.ws.preratesfilter.repository.FilterTableRepositoryImpl.getFilterData(FilterTableRepositoryImpl.java:65) ~[FilterTableRepositoryImpl.class:na]
    at com.ws.preratesfilter.repository.FilterTableRepositoryImpl$$FastClassBySpringCGLIB$$61dcca46.invoke(<generated>) ~[spring-core-4.1.8.RELEASE.jar:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.1.8.RELEASE.jar:na]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717) ~[spring-aop-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) ~[spring-aop-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at com.ws.preratesfilter.repository.FilterTableRepositoryImpl$$EnhancerBySpringCGLIB$$63aae081.getFilterData(<generated>) ~[spring-core-4.1.8.RELEASE.jar:na]
    at com.ws.preratesfilter.service.FilterTableServiceImpl.getFilterData(FilterTableServiceImpl.java:30) ~[FilterTableServiceImpl.class:na]
    at com.ws.preratesfilter.controller.FilterTableController.fetchFilterData(FilterTableController.java:34) ~[FilterTableController.class:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:775) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) ~[servlet-api.jar:na]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[servlet-api.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-websocket.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:299) ~[spring-boot-actuator-1.2.7.RELEASE.jar:1.2.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.8.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102) ~[spring-boot-actuator-1.2.7.RELEASE.jar:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.8.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.1.8.RELEASE.jar:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.8.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85) ~[spring-web-4.1.8.RELEASE.jar:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.8.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:76) ~[spring-boot-actuator-1.2.7.RELEASE.jar:1.2.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.8.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.springframework.boot.context.web.ErrorPageFilter.doFilter(ErrorPageFilter.java:116) [spring-boot-1.2.7.RELEASE.jar:na]
    at org.springframework.boot.context.web.ErrorPageFilter.access$000(ErrorPageFilter.java:60) [spring-boot-1.2.7.RELEASE.jar:na]
    at org.springframework.boot.context.web.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:91) [spring-boot-1.2.7.RELEASE.jar:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.8.RELEASE.jar:na]
    at org.springframework.boot.context.web.ErrorPageFilter.doFilter(ErrorPageFilter.java:109) [spring-boot-1.2.7.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.24]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.24]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) [catalina.jar:8.0.24]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [catalina.jar:8.0.24]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [catalina.jar:8.0.24]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) [catalina.jar:8.0.24]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [catalina.jar:8.0.24]
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617) [catalina.jar:8.0.24]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [catalina.jar:8.0.24]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) [catalina.jar:8.0.24]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) [tomcat-coyote.jar:8.0.24]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668) [tomcat-coyote.jar:8.0.24]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1527) [tomcat-coyote.jar:8.0.24]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1484) [tomcat-coyote.jar:8.0.24]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.0.24]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]
Caused by: java.sql.SQLException: Data source is closed
    at org.apache.tomcat.dbcp.dbcp2.BasicDataSource.createDataSource(BasicDataSource.java:2015) ~[tomcat-dbcp.jar:8.0.24]
    at org.apache.tomcat.dbcp.dbcp2.BasicDataSource.getConnection(BasicDataSource.java:1532) ~[tomcat-dbcp.jar:8.0.24]
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) ~[spring-jdbc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    ... 82 common frames omitted
Daniel Trimble
  • 678
  • 2
  • 6
  • 12
  • Are you doing the lookup yourself or do you let Spring Boot handle this? Can you post your application class. – M. Deinum Oct 20 '15 at 13:47
  • @M.Deinum Spring Boot is handling the lookup. We have it configured in the applilcation.properties file. – zachariahyoung Oct 20 '15 at 13:51
  • You are absolutely sure that you don't have a lookup for the datasource in the code yourself? Or any other means to make the datasource available again? Because if you do so Spring will auto detect the `close` method and call it on the bean. See https://jira.spring.io/browse/SPR-12551?jql=text%20~%20%22jndi%20datasource%22. Spring Boot counteracts this by providing an empty `shutdownMethod` for these beans, but if you override them in a way that becomes useless and close will be called anyway. – M. Deinum Oct 20 '15 at 13:52
  • Positive @M.Deinum. I'll add the application class. – Daniel Trimble Oct 20 '15 at 14:16
  • @M.Deinum we are using a NamedParameterJdbcTemplate in our repository class. Could that do the same thing you're mentioning? @Repository("filterTableRepository") public class FilterTableRepositoryImpl implements FilterTableRepository { private final Logger logger = LoggerFactory.getLogger(FilterTableRepositoryImpl.class); @Resource(name = "preRatesFilterQueriesBean") private Properties sql; private final NamedParameterJdbcTemplate namedParameterJdbcTemplate ; – Daniel Trimble Oct 20 '15 at 15:10
  • What is in your `sql.xml`? The template you are using shouldn't be doing that as it is responsible for itself not for references it uses. – M. Deinum Oct 20 '15 at 18:21
  • @M.Deinum ` <![CDATA[ ... ]]> ` – Daniel Trimble Oct 20 '15 at 19:33
  • @andy-wilkinson Do you know if something changed in Tomcat 8 that would cause the datasource to close. The issue doesn't happen on Tomcat 7. – zachariahyoung Oct 21 '15 at 04:33
  • @zachariahyoung Please add the config to the question not as comments. Also add the remark that it works on Tomcat7 . Provide all the information in the question (by modifying it). – M. Deinum Oct 21 '15 at 05:37
  • @M.Deinum done, sorry about that. – Daniel Trimble Oct 21 '15 at 10:28
  • Hey all, I've created a simple sample application that is getting the error and using an H2 jndi datasource here. https://github.com/dstrimble/ws_preRatesFilter I've placed the tomcat config files in the project base dir – Daniel Trimble Oct 22 '15 at 15:48

2 Answers2

3

Spring Framework had an issue that got fixed in the 4.2 release lines. Basically, if the bean implements Closeable its shutdown/close method is invoked even if destroyMethod is disabled (as Spring Boot does).

The reason why it works with Tomcat7 and not 8 is probably due to the fact that tomcat8 uses some of new language features (AutoCloseable). When deployed on Tomcat8, the DataSource implements this extra interface and due to the bug, the shutdown method is invoked.

You can use Spring Framework 4.2.2.RELEASE with Spring Boot 1.2.x to benefit from that fix right now. An upgrade to Spring Boot 1.3 will allow you to get that bug fix in a standard fashion.

In the meantime, just add <spring.version>4.2.2.RELEASE</spring.version> since you are using the spring-boot-starter-parent in your project.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89
3

If you cannot update to Spring Framework 4.2.x, another solution is to provide a wrapper to the datasource. Something like

    @Bean
    public DataSource getJndiDataSource() {
        try {
            JndiTemplate jndiTemplate = new JndiTemplate();
            DataSource ds = (DataSource) jndiTemplate.lookup("java:comp/env/"+JNDI_DATASOURCE);
            return new DelegatingDataSource(ds);
        } catch(NamingException e){
            LOG.error("Could not find JNDI data source (java:comp/env/):"+JNDI_DATASOURCE, e);
            return null;
        }
    }

Delegating datasource is not AutoClosable and thus will not be closed by Spring Framework.