Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a8f4708

Browse files
committedFeb 11, 2016
Shut down in-memory database when DevTools restarts the context
Previously an in-memory database that wasn’t pooled (an EmbeddedDatabase) would be shutdown when the context restarted, but an in-memory database wrapped in a connection pool was not. This meant that the former would be be wiped clean after each restart, whereas the latter would not. In addition to being inconsistent, this also caused problems with schema.sql and data.sql scripts when using DevTools. If you were using an in-memory database wrapped in a connection pool, a failure may occur during a restart as the scripts were not being run against in clean database. This commit adds an auto-configured bean to DevTools that, when the context is being closed, will execute “SHUTDOWN” if it identifies that the DataSource is not an EmbeddedDatabase and is for an in-memory database. Closes gh-4699
1 parent abf9d66 commit a8f4708

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed
 

‎spring-boot-devtools/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
<artifactId>log4j-core</artifactId>
4040
<optional>true</optional>
4141
</dependency>
42+
<dependency>
43+
<groupId>org.springframework</groupId>
44+
<artifactId>spring-jdbc</artifactId>
45+
<optional>true</optional>
46+
</dependency>
4247
<dependency>
4348
<groupId>org.springframework</groupId>
4449
<artifactId>spring-web</artifactId>
@@ -71,6 +76,11 @@
7176
<optional>true</optional>
7277
</dependency>
7378
<!-- Test -->
79+
<dependency>
80+
<groupId>com.h2database</groupId>
81+
<artifactId>h2</artifactId>
82+
<scope>test</scope>
83+
</dependency>
7484
<dependency>
7585
<groupId>org.springframework</groupId>
7686
<artifactId>spring-webmvc</artifactId>
@@ -92,6 +102,10 @@
92102
<version>${jetty.version}</version>
93103
<scope>test</scope>
94104
</dependency>
105+
<dependency>
106+
<groupId>org.postgresql</groupId>
107+
<artifactId>postgresql</artifactId>
108+
</dependency>
95109
<dependency>
96110
<groupId>org.springframework.boot</groupId>
97111
<artifactId>spring-boot-starter-thymeleaf</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.devtools.autoconfigure;
18+
19+
import java.util.Arrays;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
import javax.sql.DataSource;
24+
25+
import org.springframework.beans.factory.DisposableBean;
26+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
27+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
29+
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
31+
import org.springframework.context.annotation.Bean;
32+
import org.springframework.context.annotation.Configuration;
33+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
34+
35+
/**
36+
* {@link EnableAutoConfiguration Auto-configuration} for DevTools-specific
37+
* {@link DataSource} configuration.
38+
*
39+
* @author Andy Wilkinson
40+
* @since 1.3.3
41+
*/
42+
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
43+
@ConditionalOnBean({ DataSource.class, DataSourceProperties.class })
44+
@Configuration
45+
public class DevToolsDataSourceAutoConfiguration {
46+
47+
@Bean
48+
NonEmbeddedInMemoryDatabaseShutdownExecutor inMemoryDatabaseShutdownExecutor(
49+
DataSource dataSource, DataSourceProperties dataSourceProperties) {
50+
return new NonEmbeddedInMemoryDatabaseShutdownExecutor(dataSource,
51+
dataSourceProperties);
52+
}
53+
54+
static final class NonEmbeddedInMemoryDatabaseShutdownExecutor
55+
implements DisposableBean {
56+
57+
private static final Set<String> IN_MEMORY_DRIVER_CLASS_NAMES = new HashSet<String>(
58+
Arrays.asList("org.apache.derby.jdbc.EmbeddedDriver", "org.h2.Driver",
59+
"org.h2.jdbcx.JdbcDataSource", "org.hsqldb.jdbcDriver",
60+
"org.hsqldb.jdbc.JDBCDriver",
61+
"org.hsqldb.jdbc.pool.JDBCXADataSource"));
62+
63+
private final DataSource dataSource;
64+
65+
private final DataSourceProperties dataSourceProperties;
66+
67+
public NonEmbeddedInMemoryDatabaseShutdownExecutor(DataSource dataSource,
68+
DataSourceProperties dataSourceProperties) {
69+
this.dataSource = dataSource;
70+
this.dataSourceProperties = dataSourceProperties;
71+
}
72+
73+
@Override
74+
public void destroy() throws Exception {
75+
if (dataSourceRequiresShutdown()) {
76+
this.dataSource.getConnection().createStatement().execute("SHUTDOWN");
77+
}
78+
}
79+
80+
private boolean dataSourceRequiresShutdown() {
81+
return IN_MEMORY_DRIVER_CLASS_NAMES
82+
.contains(this.dataSourceProperties.getDriverClassName())
83+
&& (!(this.dataSource instanceof EmbeddedDatabase));
84+
}
85+
86+
}
87+
88+
}

‎spring-boot-devtools/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ org.springframework.boot.devtools.restart.RestartApplicationListener
88

99
# Auto Configure
1010
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
11+
org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration,\
1112
org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration,\
1213
org.springframework.boot.devtools.autoconfigure.RemoteDevToolsAutoConfiguration
1314

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.devtools.autoconfigure;
18+
19+
import java.sql.Connection;
20+
import java.sql.SQLException;
21+
import java.sql.Statement;
22+
23+
import javax.sql.DataSource;
24+
25+
import org.junit.Test;
26+
27+
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
28+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
29+
import org.springframework.boot.test.EnvironmentTestUtils;
30+
import org.springframework.context.ConfigurableApplicationContext;
31+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
32+
import org.springframework.context.annotation.Bean;
33+
import org.springframework.context.annotation.Configuration;
34+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
35+
36+
import static org.mockito.BDDMockito.given;
37+
import static org.mockito.Mockito.mock;
38+
import static org.mockito.Mockito.times;
39+
import static org.mockito.Mockito.verify;
40+
41+
/**
42+
* Tests for {@link DevToolsDataSourceAutoConfiguration}.
43+
*
44+
* @author Andy Wilkinson
45+
*/
46+
public class DevToolsDataSourceAutoConfigurationTests {
47+
48+
@Test
49+
public void embeddedDatabaseIsNotShutDown() throws SQLException {
50+
ConfigurableApplicationContext context = createContext("org.h2.Driver",
51+
EmbeddedDatabaseConfiguration.class);
52+
DataSource dataSource = context.getBean(DataSource.class);
53+
context.close();
54+
verify(dataSource, times(0)).getConnection();
55+
}
56+
57+
@Test
58+
public void externalDatabaseIsNotShutDown() throws SQLException {
59+
ConfigurableApplicationContext context = createContext("org.postgresql.Driver",
60+
DataSourceConfiguration.class);
61+
DataSource dataSource = context.getBean(DataSource.class);
62+
context.close();
63+
verify(dataSource, times(0)).getConnection();
64+
}
65+
66+
@Test
67+
public void nonEmbeddedInMemoryDatabaseIsShutDown() throws SQLException {
68+
ConfigurableApplicationContext context = createContext("org.h2.Driver",
69+
DataSourceConfiguration.class);
70+
DataSource dataSource = context.getBean(DataSource.class);
71+
Connection connection = mock(Connection.class);
72+
given(dataSource.getConnection()).willReturn(connection);
73+
Statement statement = mock(Statement.class);
74+
given(connection.createStatement()).willReturn(statement);
75+
context.close();
76+
verify(statement).execute("SHUTDOWN");
77+
}
78+
79+
private ConfigurableApplicationContext createContext(String driver,
80+
Class<?>... classes) {
81+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
82+
context.register(classes);
83+
context.register(DevToolsDataSourceAutoConfiguration.class);
84+
EnvironmentTestUtils.addEnvironment(context,
85+
"spring.datasource.driver-class-name:" + driver);
86+
context.refresh();
87+
return context;
88+
}
89+
90+
@Configuration
91+
@EnableConfigurationProperties(DataSourceProperties.class)
92+
static class EmbeddedDatabaseConfiguration {
93+
94+
@Bean
95+
public EmbeddedDatabase embeddedDatabase() {
96+
return mock(EmbeddedDatabase.class);
97+
}
98+
}
99+
100+
@Configuration
101+
@EnableConfigurationProperties(DataSourceProperties.class)
102+
static class DataSourceConfiguration {
103+
104+
@Bean
105+
public DataSource in() {
106+
return mock(DataSource.class);
107+
}
108+
109+
}
110+
111+
}

0 commit comments

Comments
 (0)
Please sign in to comment.