diff --git a/spring-boot-devtools/pom.xml b/spring-boot-devtools/pom.xml
index f38d55e3ae..a0633e83d3 100644
--- a/spring-boot-devtools/pom.xml
+++ b/spring-boot-devtools/pom.xml
@@ -86,6 +86,12 @@
true
+
+ org.springframework.boot
+ spring-boot
+ test-jar
+ test
+
org.springframework.boot
spring-boot-test
@@ -141,5 +147,10 @@
thymeleaf-layout-dialect
test
+
+ org.apache.tomcat
+ tomcat-jdbc
+ test
+
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
index 32b1ed6363..b07220db39 100644
--- 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
@@ -23,18 +23,24 @@ import java.util.Set;
import javax.sql.DataSource;
import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration.DevToolsDataSourceCondition;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ConfigurationCondition;
+import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@@ -109,20 +115,38 @@ public class DevToolsDataSourceAutoConfiguration {
}
- static class DevToolsDataSourceCondition extends AllNestedConditions {
+ static class DevToolsDataSourceCondition extends SpringBootCondition
+ implements ConfigurationCondition {
- DevToolsDataSourceCondition() {
- super(ConfigurationPhase.REGISTER_BEAN);
+ @Override
+ public ConfigurationPhase getConfigurationPhase() {
+ return ConfigurationPhase.REGISTER_BEAN;
}
- @ConditionalOnBean(DataSource.class)
- static final class DataSourceBean {
-
- }
-
- @ConditionalOnBean(DataSourceProperties.class)
- static final class DataSourcePropertiesBean {
-
+ @Override
+ public ConditionOutcome getMatchOutcome(ConditionContext context,
+ AnnotatedTypeMetadata metadata) {
+ String[] dataSourceBeanNames = context.getBeanFactory()
+ .getBeanNamesForType(DataSource.class);
+ if (dataSourceBeanNames.length != 1) {
+ return ConditionOutcome
+ .noMatch("A single DataSource bean was not found in the context");
+ }
+ if (context.getBeanFactory()
+ .getBeanNamesForType(DataSourceProperties.class).length != 1) {
+ return ConditionOutcome.noMatch(
+ "A single DataSourceProperties bean was not found in the context");
+ }
+ BeanDefinition dataSourceDefinition = context.getRegistry()
+ .getBeanDefinition(dataSourceBeanNames[0]);
+ if (dataSourceDefinition instanceof AnnotatedBeanDefinition
+ && ((AnnotatedBeanDefinition) dataSourceDefinition)
+ .getFactoryMethodMetadata().getDeclaringClassName()
+ .startsWith(DataSourceAutoConfiguration.class.getPackage()
+ .getName() + ".DataSourceConfiguration$")) {
+ return ConditionOutcome.match("Found auto-configured DataSource");
+ }
+ return ConditionOutcome.noMatch("DataSource was not auto-configured");
}
}
diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java
new file mode 100644
index 0000000000..ce3e725d33
--- /dev/null
+++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java
@@ -0,0 +1,161 @@
+/*
+ * 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 java.util.Collection;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.util.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 static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Base class for tests for {@link DevToolsDataSourceAutoConfiguration}.
+ *
+ * @author Andy Wilkinson
+ */
+public class AbstractDevToolsDataSourceAutoConfigurationTests {
+
+ @Test
+ public void singleManuallyConfiguredDataSourceIsNotClosed() throws SQLException {
+ ConfigurableApplicationContext context = createContext(
+ DataSourcePropertiesConfiguration.class,
+ SingleDataSourceConfiguration.class);
+ DataSource dataSource = context.getBean(DataSource.class);
+ Statement statement = configureDataSourceBehaviour(dataSource);
+ verify(statement, times(0)).execute("SHUTDOWN");
+ }
+
+ @Test
+ public void multipleDataSourcesAreIgnored() throws SQLException {
+ ConfigurableApplicationContext context = createContext(
+ DataSourcePropertiesConfiguration.class,
+ MultipleDataSourcesConfiguration.class);
+ Collection dataSources = context.getBeansOfType(DataSource.class)
+ .values();
+ for (DataSource dataSource : dataSources) {
+ Statement statement = configureDataSourceBehaviour(dataSource);
+ verify(statement, times(0)).execute("SHUTDOWN");
+ }
+ }
+
+ protected final Statement configureDataSourceBehaviour(DataSource dataSource)
+ throws SQLException {
+ Connection connection = mock(Connection.class);
+ Statement statement = mock(Statement.class);
+ doReturn(connection).when(dataSource).getConnection();
+ given(connection.createStatement()).willReturn(statement);
+ return statement;
+ }
+
+ protected final ConfigurableApplicationContext createContext(String driverClassName,
+ Class>... classes) {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.register(classes);
+ context.register(DevToolsDataSourceAutoConfiguration.class);
+ if (driverClassName != null) {
+ EnvironmentTestUtils.addEnvironment(context,
+ "spring.datasource.driver-class-name:" + driverClassName);
+ }
+ context.refresh();
+ return context;
+ }
+
+ protected final ConfigurableApplicationContext createContext(Class>... classes) {
+ return this.createContext(null, classes);
+ }
+
+ @Configuration
+ static class SingleDataSourceConfiguration {
+
+ @Bean
+ public DataSource dataSource() {
+ return mock(DataSource.class);
+ }
+
+ }
+
+ @Configuration
+ static class MultipleDataSourcesConfiguration {
+
+ @Bean
+ public DataSource dataSourceOne() {
+ return mock(DataSource.class);
+ }
+
+ @Bean
+ public DataSource dataSourceTwo() {
+ return mock(DataSource.class);
+ }
+
+ }
+
+ @Configuration
+ @EnableConfigurationProperties(DataSourceProperties.class)
+ static class DataSourcePropertiesConfiguration {
+
+ }
+
+ @Configuration
+ static class DataSourceSpyConfiguration {
+
+ @Bean
+ public DataSourceSpyBeanPostProcessor dataSourceSpyBeanPostProcessor() {
+ return new DataSourceSpyBeanPostProcessor();
+ }
+
+ }
+
+ private static class DataSourceSpyBeanPostProcessor implements BeanPostProcessor {
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof DataSource) {
+ bean = spy(bean);
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ }
+
+}
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
deleted file mode 100644
index b1248c5885..0000000000
--- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsDataSourceAutoConfigurationTests.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.persistence.EntityManagerFactory;
-import javax.sql.DataSource;
-
-import org.junit.Test;
-import org.mockito.InOrder;
-
-import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.test.util.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.assertj.core.api.Assertions.assertThat;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.inOrder;
-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 = createContextWithDriver("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 = createContextWithDriver(
- "org.postgresql.Driver", DataSourceConfiguration.class);
- DataSource dataSource = context.getBean(DataSource.class);
- context.close();
- verify(dataSource, times(0)).getConnection();
- }
-
- @Test
- public void nonEmbeddedInMemoryDatabaseConfiguredWithDriverIsShutDown()
- throws SQLException {
- ConfigurableApplicationContext context = createContextWithDriver("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");
- }
-
- @Test
- public void nonEmbeddedInMemoryDatabaseConfiguredWithUrlIsShutDown()
- throws SQLException {
- ConfigurableApplicationContext context = createContextWithUrl("jdbc:h2:mem:test",
- 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");
- }
-
- @Test
- public void configurationBacksOffWithoutDataSourceProperties() throws SQLException {
- ConfigurableApplicationContext context = createContext("org.h2.Driver",
- NoDataSourcePropertiesConfiguration.class);
- assertThat(context.getBeansOfType(DevToolsDataSourceAutoConfiguration.class))
- .isEmpty();
- }
-
- @Test
- public void entityManagerFactoryIsClosedBeforeDatabaseIsShutDown()
- throws SQLException {
- ConfigurableApplicationContext context = createContextWithUrl("jdbc:h2:mem:test",
- DataSourceConfiguration.class, EntityManagerFactoryConfiguration.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);
- EntityManagerFactory entityManagerFactory = context
- .getBean(EntityManagerFactory.class);
- context.close();
- InOrder inOrder = inOrder(statement, entityManagerFactory);
- inOrder.verify(statement).execute("SHUTDOWN");
- inOrder.verify(entityManagerFactory).close();
- }
-
- private ConfigurableApplicationContext createContextWithDriver(String driver,
- Class>... classes) {
- return createContext("spring.datasource.driver-class-name:" + driver, classes);
- }
-
- private ConfigurableApplicationContext createContextWithUrl(String url,
- Class>... classes) {
- return createContext("spring.datasource.url:" + url, classes);
- }
-
- private ConfigurableApplicationContext createContext(String property,
- Class>... classes) {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.register(classes);
- context.register(DevToolsDataSourceAutoConfiguration.class);
- EnvironmentTestUtils.addEnvironment(context, property);
- 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 dataSource() {
- return mock(DataSource.class);
- }
-
- }
-
- @Configuration
- static class NoDataSourcePropertiesConfiguration {
-
- @Bean
- public DataSource dataSource() {
- return mock(DataSource.class);
- }
-
- }
-
- @Configuration
- static class EntityManagerFactoryConfiguration {
-
- @Bean
- public EntityManagerFactory entityManagerFactory() {
- return mock(EntityManagerFactory.class);
- }
-
- }
-
-}
diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java
new file mode 100644
index 0000000000..4442faa9ff
--- /dev/null
+++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java
@@ -0,0 +1,55 @@
+/*
+ * 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.SQLException;
+import java.sql.Statement;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.testutil.ClassPathExclusions;
+import org.springframework.boot.testutil.FilteredClassPathRunner;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link DevToolsDataSourceAutoConfiguration} with an embedded data source.
+ *
+ * @author Andy Wilkinson
+ */
+@RunWith(FilteredClassPathRunner.class)
+@ClassPathExclusions("tomcat-jdbc-*.jar")
+public class DevToolsEmbeddedDataSourceAutoConfigurationTests
+ extends AbstractDevToolsDataSourceAutoConfigurationTests {
+
+ @Test
+ public void autoConfiguredDataSourceIsNotShutdown() throws SQLException {
+ ConfigurableApplicationContext context = createContext(
+ DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
+ Statement statement = configureDataSourceBehaviour(
+ context.getBean(DataSource.class));
+ context.close();
+ verify(statement, times(0)).execute("SHUTDOWN");
+ }
+
+}
diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java
new file mode 100644
index 0000000000..3d1612bb19
--- /dev/null
+++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java
@@ -0,0 +1,63 @@
+/*
+ * 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.SQLException;
+import java.sql.Statement;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.testutil.FilteredClassPathRunner;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link DevToolsDataSourceAutoConfiguration} with a pooled data source.
+ *
+ * @author Andy Wilkinson
+ */
+@RunWith(FilteredClassPathRunner.class)
+public class DevToolsPooledDataSourceAutoConfigurationTests
+ extends AbstractDevToolsDataSourceAutoConfigurationTests {
+
+ @Test
+ public void autoConfiguredInMemoryDataSourceIsShutdown() throws SQLException {
+ ConfigurableApplicationContext context = createContext(
+ DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
+ Statement statement = configureDataSourceBehaviour(
+ context.getBean(DataSource.class));
+ context.close();
+ verify(statement).execute("SHUTDOWN");
+ }
+
+ @Test
+ public void autoConfiguredExternalDataSourceIsNotShutdown() throws SQLException {
+ ConfigurableApplicationContext context = createContext("org.postgresql.Driver",
+ DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
+ Statement statement = configureDataSourceBehaviour(
+ context.getBean(DataSource.class));
+ context.close();
+ verify(statement, times(0)).execute("SHUTDOWN");
+ }
+
+}