Auto-configure Flyway and Liquibase when there's a URL but no DataSource
See gh-16850
This commit is contained in:
committed by
Andy Wilkinson
parent
f59e337aba
commit
3ca73bf00d
@@ -82,8 +82,8 @@ import org.springframework.util.StringUtils;
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Flyway.class)
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
@ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
|
||||
@ConditionalOnProperty(prefix = FlywayProperties.PROPERTIES_PREFIX, name = "enabled",
|
||||
matchIfMissing = true)
|
||||
@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
|
||||
JdbcTemplateAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
||||
public class FlywayAutoConfiguration {
|
||||
@@ -152,9 +152,12 @@ public class FlywayAutoConfiguration {
|
||||
else if (flywayDataSource != null) {
|
||||
configuration.dataSource(flywayDataSource);
|
||||
}
|
||||
else {
|
||||
else if (dataSource != null) {
|
||||
configuration.dataSource(dataSource);
|
||||
}
|
||||
else {
|
||||
throw new FlywayDataSourceMissingException();
|
||||
}
|
||||
return configuration.getDataSource();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.autoconfigure.flyway;
|
||||
|
||||
/**
|
||||
* Exception thrown when no Flyway is enabled and present in classpath but no datasource
|
||||
* was available or configured for it.
|
||||
*
|
||||
* @author Ilya Lukyanovich
|
||||
*/
|
||||
public class FlywayDataSourceMissingException extends RuntimeException {
|
||||
|
||||
FlywayDataSourceMissingException() {
|
||||
super("Flyway is present in classpath and enabled but "
|
||||
+ "no DataSource bean neither " + FlywayProperties.PROPERTIES_PREFIX
|
||||
+ ".url and " + FlywayProperties.PROPERTIES_PREFIX
|
||||
+ ".user configuration was provided");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.autoconfigure.flyway;
|
||||
|
||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
|
||||
/**
|
||||
* A {@code FailureAnalyzer} that performs analysis of failures caused by a
|
||||
* {@link FlywayDataSourceMissingException}.
|
||||
*
|
||||
* @author Ilya Lukyanovich
|
||||
*/
|
||||
class FlywayDataSourceMissingFailureAnalyzer
|
||||
extends AbstractFailureAnalyzer<FlywayDataSourceMissingException> {
|
||||
|
||||
@Override
|
||||
protected FailureAnalysis analyze(Throwable rootFailure,
|
||||
FlywayDataSourceMissingException cause) {
|
||||
return new FailureAnalysis("No DataSource found for flyway",
|
||||
"Please provide a javax.sql.DataSource bean or flyway datasource configuration",
|
||||
cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,9 +35,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.flyway")
|
||||
@ConfigurationProperties(prefix = FlywayProperties.PROPERTIES_PREFIX)
|
||||
public class FlywayProperties {
|
||||
|
||||
public static final String PROPERTIES_PREFIX = "spring.flyway";
|
||||
|
||||
/**
|
||||
* Whether to enable flyway.
|
||||
*/
|
||||
|
||||
@@ -65,8 +65,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ SpringLiquibase.class, DatabaseChange.class })
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled",
|
||||
@ConditionalOnProperty(prefix = LiquibaseProperties.PROPERTIES_PREFIX, name = "enabled",
|
||||
matchIfMissing = true)
|
||||
@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class })
|
||||
@@ -141,9 +140,12 @@ public class LiquibaseAutoConfiguration {
|
||||
liquibase.setDataSource(liquibaseDataSource);
|
||||
return liquibase;
|
||||
}
|
||||
SpringLiquibase liquibase = new DataSourceClosingSpringLiquibase();
|
||||
liquibase.setDataSource(createNewDataSource(dataSourceProperties));
|
||||
return liquibase;
|
||||
else if (this.properties.isCreateDataSource()) {
|
||||
SpringLiquibase liquibase = new DataSourceClosingSpringLiquibase();
|
||||
liquibase.setDataSource(createNewDataSource(dataSourceProperties));
|
||||
return liquibase;
|
||||
}
|
||||
throw new LiquibaseDataSourceMissingException();
|
||||
}
|
||||
|
||||
private DataSource getDataSource(DataSource liquibaseDataSource,
|
||||
@@ -151,7 +153,7 @@ public class LiquibaseAutoConfiguration {
|
||||
if (liquibaseDataSource != null) {
|
||||
return liquibaseDataSource;
|
||||
}
|
||||
if (this.properties.getUrl() == null && this.properties.getUser() == null) {
|
||||
if (!this.properties.isCreateDataSource()) {
|
||||
return dataSource;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.autoconfigure.liquibase;
|
||||
|
||||
/**
|
||||
* Exception thrown when Liquibase is enabled and present in classpath but no datasource
|
||||
* was available or configured for it.
|
||||
*
|
||||
* @author Ilya Lukyanovich
|
||||
*/
|
||||
public class LiquibaseDataSourceMissingException extends RuntimeException {
|
||||
|
||||
LiquibaseDataSourceMissingException() {
|
||||
super("Liquibase is present in classpath and enabled but "
|
||||
+ "no DataSource bean neither " + LiquibaseProperties.PROPERTIES_PREFIX
|
||||
+ ".url and " + LiquibaseProperties.PROPERTIES_PREFIX
|
||||
+ ".user configuration was provided");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.autoconfigure.liquibase;
|
||||
|
||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
|
||||
/**
|
||||
* A {@code FailureAnalyzer} that performs analysis of failures caused by a
|
||||
* {@link LiquibaseDataSourceMissingException}.
|
||||
*
|
||||
* @author Ilya Lukyanovich
|
||||
*/
|
||||
class LiquibaseDataSourceMissingFailureAnalyzer
|
||||
extends AbstractFailureAnalyzer<LiquibaseDataSourceMissingException> {
|
||||
|
||||
@Override
|
||||
protected FailureAnalysis analyze(Throwable rootFailure,
|
||||
LiquibaseDataSourceMissingException cause) {
|
||||
return new FailureAnalysis("No DataSource found for liquibsae",
|
||||
"Please provide a javax.sql.DataSource bean or liquibase datasource configuration",
|
||||
cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,9 +30,12 @@ import org.springframework.util.Assert;
|
||||
* @author Marcel Overdijk
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.liquibase", ignoreUnknownFields = false)
|
||||
@ConfigurationProperties(prefix = LiquibaseProperties.PROPERTIES_PREFIX,
|
||||
ignoreUnknownFields = false)
|
||||
public class LiquibaseProperties {
|
||||
|
||||
public static final String PROPERTIES_PREFIX = "spring.liquibase";
|
||||
|
||||
/**
|
||||
* Change log configuration path.
|
||||
*/
|
||||
@@ -216,6 +219,10 @@ public class LiquibaseProperties {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public boolean isCreateDataSource() {
|
||||
return this.url != null || this.user != null;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
@@ -146,6 +146,8 @@ org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAuto
|
||||
org.springframework.boot.diagnostics.FailureAnalyzer=\
|
||||
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
|
||||
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
|
||||
org.springframework.boot.autoconfigure.flyway.FlywayDataSourceMissingFailureAnalyzer,\
|
||||
org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSourceMissingFailureAnalyzer,\
|
||||
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
|
||||
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
|
||||
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
|
||||
|
||||
@@ -80,8 +80,25 @@ public class FlywayAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void noDataSource() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context).getFailure().isInstanceOf(BeanCreationException.class);
|
||||
assertThat(context).getFailure()
|
||||
.hasRootCauseInstanceOf(FlywayDataSourceMissingException.class);
|
||||
assertThat(context).getFailure()
|
||||
.hasMessageContaining("Flyway is present in classpath and enabled");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDataSourceCreateOneWithUrl() {
|
||||
this.contextRunner
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(Flyway.class));
|
||||
.withPropertyValues(
|
||||
"spring.flyway.url:jdbc:hsqldb:mem:" + UUID.randomUUID())
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
assertThat(context.getBean(Flyway.class).getDataSource()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -79,8 +79,26 @@ public class LiquibaseAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void noDataSource() {
|
||||
this.contextRunner.run(
|
||||
(context) -> assertThat(context).doesNotHaveBean(SpringLiquibase.class));
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context).getFailure().isInstanceOf(BeanCreationException.class);
|
||||
assertThat(context).getFailure()
|
||||
.hasRootCauseInstanceOf(LiquibaseDataSourceMissingException.class);
|
||||
assertThat(context).getFailure().hasMessageContaining(
|
||||
"Liquibase is present in classpath and enabled");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDataSourceCreateOneWithUrl() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:liquibase")
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
DataSource dataSource = liquibase.getDataSource();
|
||||
assertThat(((HikariDataSource) dataSource).isClosed()).isTrue();
|
||||
assertThat(((HikariDataSource) dataSource).getJdbcUrl())
|
||||
.isEqualTo("jdbc:hsqldb:mem:liquibase");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayProperties
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties
|
||||
import org.springframework.boot.configurationdocs.ConfigurationMetadataDocumentWriter
|
||||
import org.springframework.boot.configurationdocs.DocumentOptions
|
||||
import org.springframework.core.io.UrlResource
|
||||
@@ -48,7 +50,7 @@ def generateConfigMetadataDocumentation() {
|
||||
.addSection("security")
|
||||
.withKeyPrefixes("spring.security", "spring.ldap", "spring.session")
|
||||
.addSection("data-migration")
|
||||
.withKeyPrefixes("spring.flyway", "spring.liquibase")
|
||||
.withKeyPrefixes(FlywayProperties.PROPERTIES_PREFIX, LiquibaseProperties.PROPERTIES_PREFIX)
|
||||
.addSection("data")
|
||||
.withKeyPrefixes("spring.couchbase", "spring.elasticsearch", "spring.h2",
|
||||
"spring.influx", "spring.mongodb", "spring.redis",
|
||||
|
||||
Reference in New Issue
Block a user