diff --git a/spring-boot-devtools/pom.xml b/spring-boot-devtools/pom.xml index 495f1e3f52b..30b67cc4eaa 100644 --- a/spring-boot-devtools/pom.xml +++ b/spring-boot-devtools/pom.xml @@ -39,6 +39,11 @@ log4j-core true + + org.springframework + spring-jdbc + true + org.springframework spring-web @@ -71,6 +76,11 @@ true + + com.h2database + h2 + test + org.springframework spring-webmvc @@ -92,6 +102,10 @@ ${jetty.version} test + + org.postgresql + postgresql + org.springframework.boot spring-boot-starter-thymeleaf diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java new file mode 100644 index 00000000000..549e6e14dee --- /dev/null +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfiguration.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.devtools.autoconfigure; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for DevTools-specific + * {@link DataSource} configuration. + * + * @author Andy Wilkinson + * @since 1.3.3 + */ +@AutoConfigureAfter(DataSourceAutoConfiguration.class) +@ConditionalOnBean({ DataSource.class, DataSourceProperties.class }) +@Configuration +public class DevToolsDataSourceAutoConfiguration { + + @Bean + NonEmbeddedInMemoryDatabaseShutdownExecutor inMemoryDatabaseShutdownExecutor( + DataSource dataSource, DataSourceProperties dataSourceProperties) { + return new NonEmbeddedInMemoryDatabaseShutdownExecutor(dataSource, + dataSourceProperties); + } + + static final class NonEmbeddedInMemoryDatabaseShutdownExecutor + implements DisposableBean { + + private static final Set IN_MEMORY_DRIVER_CLASS_NAMES = new HashSet( + Arrays.asList("org.apache.derby.jdbc.EmbeddedDriver", "org.h2.Driver", + "org.h2.jdbcx.JdbcDataSource", "org.hsqldb.jdbcDriver", + "org.hsqldb.jdbc.JDBCDriver", + "org.hsqldb.jdbc.pool.JDBCXADataSource")); + + private final DataSource dataSource; + + private final DataSourceProperties dataSourceProperties; + + public NonEmbeddedInMemoryDatabaseShutdownExecutor(DataSource dataSource, + DataSourceProperties dataSourceProperties) { + this.dataSource = dataSource; + this.dataSourceProperties = dataSourceProperties; + } + + @Override + public void destroy() throws Exception { + if (dataSourceRequiresShutdown()) { + this.dataSource.getConnection().createStatement().execute("SHUTDOWN"); + } + } + + private boolean dataSourceRequiresShutdown() { + return IN_MEMORY_DRIVER_CLASS_NAMES + .contains(this.dataSourceProperties.getDriverClassName()) + && (!(this.dataSource instanceof EmbeddedDatabase)); + } + + } + +} diff --git a/spring-boot-devtools/src/main/resources/META-INF/spring.factories b/spring-boot-devtools/src/main/resources/META-INF/spring.factories index fc66a3dcdb6..6afcc572994 100644 --- a/spring-boot-devtools/src/main/resources/META-INF/spring.factories +++ b/spring-boot-devtools/src/main/resources/META-INF/spring.factories @@ -8,6 +8,7 @@ org.springframework.boot.devtools.restart.RestartApplicationListener # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration,\ org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration,\ org.springframework.boot.devtools.autoconfigure.RemoteDevToolsAutoConfiguration diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfigurationTests.java new file mode 100644 index 00000000000..57c5a340d48 --- /dev/null +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfigurationTests.java @@ -0,0 +1,111 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.devtools.autoconfigure; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.DataSource; + +import org.junit.Test; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link DevToolsDataSourceAutoConfiguration}. + * + * @author Andy Wilkinson + */ +public class DevToolsDataSourceAutoConfigurationTests { + + @Test + public void embeddedDatabaseIsNotShutDown() throws SQLException { + ConfigurableApplicationContext context = createContext("org.h2.Driver", + EmbeddedDatabaseConfiguration.class); + DataSource dataSource = context.getBean(DataSource.class); + context.close(); + verify(dataSource, times(0)).getConnection(); + } + + @Test + public void externalDatabaseIsNotShutDown() throws SQLException { + ConfigurableApplicationContext context = createContext("org.postgresql.Driver", + DataSourceConfiguration.class); + DataSource dataSource = context.getBean(DataSource.class); + context.close(); + verify(dataSource, times(0)).getConnection(); + } + + @Test + public void nonEmbeddedInMemoryDatabaseIsShutDown() throws SQLException { + ConfigurableApplicationContext context = createContext("org.h2.Driver", + DataSourceConfiguration.class); + DataSource dataSource = context.getBean(DataSource.class); + Connection connection = mock(Connection.class); + given(dataSource.getConnection()).willReturn(connection); + Statement statement = mock(Statement.class); + given(connection.createStatement()).willReturn(statement); + context.close(); + verify(statement).execute("SHUTDOWN"); + } + + private ConfigurableApplicationContext createContext(String driver, + Class... classes) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(classes); + context.register(DevToolsDataSourceAutoConfiguration.class); + EnvironmentTestUtils.addEnvironment(context, + "spring.datasource.driver-class-name:" + driver); + context.refresh(); + return context; + } + + @Configuration + @EnableConfigurationProperties(DataSourceProperties.class) + static class EmbeddedDatabaseConfiguration { + + @Bean + public EmbeddedDatabase embeddedDatabase() { + return mock(EmbeddedDatabase.class); + } + } + + @Configuration + @EnableConfigurationProperties(DataSourceProperties.class) + static class DataSourceConfiguration { + + @Bean + public DataSource in() { + return mock(DataSource.class); + } + + } + +}