Skip to content

Allow Flyway or Liquibase to be auto-configured without a DataSource bean #16850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@
@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 {
Expand Down Expand Up @@ -151,9 +151,12 @@ private DataSource configureDataSource(FluentConfiguration configuration,
else if (flywayDataSource != null) {
configuration.dataSource(flywayDataSource);
}
else {
else if (dataSource != null) {
configuration.dataSource(dataSource);
}
else {
throw new FlywayDataSourceMissingException();
}
return configuration.getDataSource();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
* @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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@
*/
@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 })
Expand Down Expand Up @@ -141,17 +140,20 @@ private SpringLiquibase createSpringLiquibase(DataSource liquibaseDatasource,
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,
DataSource dataSource) {
if (liquibaseDataSource != null) {
return liquibaseDataSource;
}
if (this.properties.getUrl() == null && this.properties.getUser() == null) {
if (!this.properties.isCreateDataSource()) {
return dataSource;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@
* @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.
*/
Expand Down Expand Up @@ -216,6 +219,10 @@ public void setPassword(String password) {
this.password = password;
}

public boolean isCreateDataSource() {
return this.url != null || this.user != null;
}

public String getUrl() {
return this.url;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,26 @@ public void init() {

@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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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",
Expand Down