Commit d1dc8cb7 authored by Andy Wilkinson's avatar Andy Wilkinson

Merge pull request #11751 from Dominic Gunn

* gh-11751:
  Polish "Use custom DataSource if Flyway or Liquibase has user or url"
  Use custom DataSource if Flyway or Liquibase has user or url
parents 24ec4417 192fe929
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -22,6 +22,7 @@ import java.util.Collections; ...@@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
...@@ -39,6 +40,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean ...@@ -39,6 +40,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor; import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
...@@ -65,6 +67,7 @@ import org.springframework.util.ObjectUtils; ...@@ -65,6 +67,7 @@ import org.springframework.util.ObjectUtils;
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Jacques-Etienne Beaudet * @author Jacques-Etienne Beaudet
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Dominic Gunn
* @since 1.1.0 * @since 1.1.0
*/ */
@Configuration @Configuration
...@@ -95,6 +98,8 @@ public class FlywayAutoConfiguration { ...@@ -95,6 +98,8 @@ public class FlywayAutoConfiguration {
private final FlywayProperties properties; private final FlywayProperties properties;
private final DataSourceProperties dataSourceProperties;
private final ResourceLoader resourceLoader; private final ResourceLoader resourceLoader;
private final DataSource dataSource; private final DataSource dataSource;
...@@ -106,11 +111,13 @@ public class FlywayAutoConfiguration { ...@@ -106,11 +111,13 @@ public class FlywayAutoConfiguration {
private List<FlywayCallback> flywayCallbacks; private List<FlywayCallback> flywayCallbacks;
public FlywayConfiguration(FlywayProperties properties, public FlywayConfiguration(FlywayProperties properties,
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource, DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader,
ObjectProvider<DataSource> dataSource,
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource, @FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy, ObjectProvider<FlywayMigrationStrategy> migrationStrategy,
ObjectProvider<List<FlywayCallback>> flywayCallbacks) { ObjectProvider<List<FlywayCallback>> flywayCallbacks) {
this.properties = properties; this.properties = properties;
this.dataSourceProperties = dataSourceProperties;
this.resourceLoader = resourceLoader; this.resourceLoader = resourceLoader;
this.dataSource = dataSource.getIfUnique(); this.dataSource = dataSource.getIfUnique();
this.flywayDataSource = flywayDataSource.getIfAvailable(); this.flywayDataSource = flywayDataSource.getIfAvailable();
...@@ -123,8 +130,13 @@ public class FlywayAutoConfiguration { ...@@ -123,8 +130,13 @@ public class FlywayAutoConfiguration {
public Flyway flyway() { public Flyway flyway() {
Flyway flyway = new SpringBootFlyway(); Flyway flyway = new SpringBootFlyway();
if (this.properties.isCreateDataSource()) { if (this.properties.isCreateDataSource()) {
flyway.setDataSource(this.properties.getUrl(), this.properties.getUser(), String url = getProperty(this.properties::getUrl,
this.properties.getPassword(), this.dataSourceProperties::getUrl);
String user = getProperty(this.properties::getUser,
this.dataSourceProperties::getUsername);
String password = getProperty(this.properties::getPassword,
this.dataSourceProperties::getPassword);
flyway.setDataSource(url, user, password,
this.properties.getInitSqls().toArray(new String[0])); this.properties.getInitSqls().toArray(new String[0]));
} }
else if (this.flywayDataSource != null) { else if (this.flywayDataSource != null) {
...@@ -142,6 +154,12 @@ public class FlywayAutoConfiguration { ...@@ -142,6 +154,12 @@ public class FlywayAutoConfiguration {
return flyway; return flyway;
} }
private String getProperty(Supplier<String> property,
Supplier<String> defaultValue) {
String value = property.get();
return value == null ? defaultValue.get() : value;
}
private void checkLocationExists(String... locations) { private void checkLocationExists(String... locations) {
if (this.properties.isCheckLocation()) { if (this.properties.isCheckLocation()) {
Assert.state(locations.length != 0, Assert.state(locations.length != 0,
......
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -133,7 +133,7 @@ public class FlywayProperties { ...@@ -133,7 +133,7 @@ public class FlywayProperties {
} }
public boolean isCreateDataSource() { public boolean isCreateDataSource() {
return this.url != null && this.user != null; return this.url != null || this.user != null;
} }
} }
...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.liquibase; ...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.liquibase;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
...@@ -37,6 +38,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean ...@@ -37,6 +38,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor; import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceBuilder;
...@@ -58,6 +60,7 @@ import org.springframework.util.ReflectionUtils; ...@@ -58,6 +60,7 @@ import org.springframework.util.ReflectionUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Dominic Gunn
* @since 1.1.0 * @since 1.1.0
*/ */
@Configuration @Configuration
...@@ -83,6 +86,8 @@ public class LiquibaseAutoConfiguration { ...@@ -83,6 +86,8 @@ public class LiquibaseAutoConfiguration {
private final LiquibaseProperties properties; private final LiquibaseProperties properties;
private final DataSourceProperties dataSourceProperties;
private final ResourceLoader resourceLoader; private final ResourceLoader resourceLoader;
private final DataSource dataSource; private final DataSource dataSource;
...@@ -90,9 +95,11 @@ public class LiquibaseAutoConfiguration { ...@@ -90,9 +95,11 @@ public class LiquibaseAutoConfiguration {
private final DataSource liquibaseDataSource; private final DataSource liquibaseDataSource;
public LiquibaseConfiguration(LiquibaseProperties properties, public LiquibaseConfiguration(LiquibaseProperties properties,
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource, DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader,
ObjectProvider<DataSource> dataSource,
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource) { @LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource) {
this.properties = properties; this.properties = properties;
this.dataSourceProperties = dataSourceProperties;
this.resourceLoader = resourceLoader; this.resourceLoader = resourceLoader;
this.dataSource = dataSource.getIfUnique(); this.dataSource = dataSource.getIfUnique();
this.liquibaseDataSource = liquibaseDataSource.getIfAvailable(); this.liquibaseDataSource = liquibaseDataSource.getIfAvailable();
...@@ -140,16 +147,27 @@ public class LiquibaseAutoConfiguration { ...@@ -140,16 +147,27 @@ public class LiquibaseAutoConfiguration {
if (this.liquibaseDataSource != null) { if (this.liquibaseDataSource != null) {
return this.liquibaseDataSource; return this.liquibaseDataSource;
} }
if (this.properties.getUrl() == null) { if (this.properties.getUrl() == null && this.properties.getUser() == null) {
return this.dataSource; return this.dataSource;
} }
return null; return null;
} }
private DataSource createNewDataSource() { private DataSource createNewDataSource() {
return DataSourceBuilder.create().url(this.properties.getUrl()) String url = getProperty(this.properties::getUrl,
.username(this.properties.getUser()) this.dataSourceProperties::getUrl);
.password(this.properties.getPassword()).build(); String user = getProperty(this.properties::getUser,
this.dataSourceProperties::getUsername);
String password = getProperty(this.properties::getPassword,
this.dataSourceProperties::getPassword);
return DataSourceBuilder.create().url(url).username(user).password(password)
.build();
}
private String getProperty(Supplier<String> property,
Supplier<String> defaultValue) {
String value = property.get();
return value == null ? defaultValue.get() : value;
} }
} }
......
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -60,6 +60,7 @@ import static org.mockito.Mockito.mock; ...@@ -60,6 +60,7 @@ import static org.mockito.Mockito.mock;
* @author Vedran Pavic * @author Vedran Pavic
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Dominic Gunn
*/ */
public class FlywayAutoConfigurationTests { public class FlywayAutoConfigurationTests {
...@@ -74,9 +75,19 @@ public class FlywayAutoConfigurationTests { ...@@ -74,9 +75,19 @@ public class FlywayAutoConfigurationTests {
} }
@Test @Test
public void createDataSource() { public void createDataSourceWithUrl() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:flywaytest", .withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:flywaytest")
.run((context) -> {
assertThat(context).hasSingleBean(Flyway.class);
assertThat(context.getBean(Flyway.class).getDataSource()).isNotNull();
});
}
@Test
public void createDataSourceWithUser() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.datasource.url:jdbc:hsqldb:mem:normal",
"spring.flyway.user:sa") "spring.flyway.user:sa")
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
......
...@@ -58,6 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -58,6 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Dominic Gunn
*/ */
public class LiquibaseAutoConfigurationTests { public class LiquibaseAutoConfigurationTests {
...@@ -153,8 +154,7 @@ public class LiquibaseAutoConfigurationTests { ...@@ -153,8 +154,7 @@ public class LiquibaseAutoConfigurationTests {
@Test @Test
public void overrideDataSource() { public void overrideDataSource() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:liquibase", .withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:liquibase")
"spring.liquibase.user:sa")
.run(assertLiquibase((liquibase) -> { .run(assertLiquibase((liquibase) -> {
DataSource dataSource = liquibase.getDataSource(); DataSource dataSource = liquibase.getDataSource();
assertThat(((HikariDataSource) dataSource).isClosed()).isTrue(); assertThat(((HikariDataSource) dataSource).isClosed()).isTrue();
...@@ -163,6 +163,21 @@ public class LiquibaseAutoConfigurationTests { ...@@ -163,6 +163,21 @@ public class LiquibaseAutoConfigurationTests {
})); }));
} }
@Test
public void overrideUser() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
.withPropertyValues("spring.datasource.url:jdbc:hsqldb:mem:normal",
"spring.datasource.username:not-sa", "spring.liquibase.user:sa")
.run(assertLiquibase((liquibase) -> {
DataSource dataSource = liquibase.getDataSource();
assertThat(((HikariDataSource) dataSource).isClosed()).isTrue();
assertThat(((HikariDataSource) dataSource).getJdbcUrl())
.isEqualTo("jdbc:hsqldb:mem:normal");
assertThat(((HikariDataSource) dataSource).getUsername())
.isEqualTo("sa");
}));
}
@Test @Test
public void changeLogDoesNotExist() { public void changeLogDoesNotExist() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
......
...@@ -2139,7 +2139,10 @@ uses that for migrations. If you like to use a different `DataSource`, you can c ...@@ -2139,7 +2139,10 @@ uses that for migrations. If you like to use a different `DataSource`, you can c
one and mark its `@Bean` as `@FlywayDataSource`. If you do so and want two data sources, one and mark its `@Bean` as `@FlywayDataSource`. If you do so and want two data sources,
remember to create another one and mark it as `@Primary`. Alternatively, you can use remember to create another one and mark it as `@Primary`. Alternatively, you can use
Flyway's native `DataSource` by setting `spring.flyway.[url,user,password]` Flyway's native `DataSource` by setting `spring.flyway.[url,user,password]`
in external properties. in external properties. Setting either `spring.flyway.url` or `spring.flyway.user`
is sufficent to cause Flyway to use its own `DataSource`. If any of the three
properties has not be set, the value of its equivalent `spring.datasource` property will
be used.
There is a {github-code}/spring-boot-samples/spring-boot-sample-flyway[Flyway sample] so There is a {github-code}/spring-boot-samples/spring-boot-sample-flyway[Flyway sample] so
that you can see how to set things up. that you can see how to set things up.
...@@ -2175,7 +2178,10 @@ that for migrations. If you need to use a different `DataSource`, you can create ...@@ -2175,7 +2178,10 @@ that for migrations. If you need to use a different `DataSource`, you can create
mark its `@Bean` as `@LiquibaseDataSource`. If you do so and you want two data sources, mark its `@Bean` as `@LiquibaseDataSource`. If you do so and you want two data sources,
remember to create another one and mark it as `@Primary`. Alternatively, you can use remember to create another one and mark it as `@Primary`. Alternatively, you can use
Liquibase's native `DataSource` by setting `spring.liquibase.[url,user,password]` in Liquibase's native `DataSource` by setting `spring.liquibase.[url,user,password]` in
external properties. external properties. Setting either `spring.liquibase.url` or `spring.liquibase.user`
is sufficent to cause Liquibase to use its own `DataSource`. If any of the three
properties has not be set, the value of its equivalent `spring.datasource` property will
be used.
See See
{sc-spring-boot-autoconfigure}/liquibase/LiquibaseProperties.{sc-ext}[`LiquibaseProperties`] {sc-spring-boot-autoconfigure}/liquibase/LiquibaseProperties.{sc-ext}[`LiquibaseProperties`]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment